diff --git a/.gitignore b/.gitignore index 7eeb9883c..c3c04174c 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,4 @@ build *.log .vscode .envrc - +.DS_Store diff --git a/src/cmake/testing.cmake b/src/cmake/testing.cmake index 14112e31c..f7aced970 100644 --- a/src/cmake/testing.cmake +++ b/src/cmake/testing.cmake @@ -127,6 +127,7 @@ macro ( TESTSUITE ) # Run the test unoptimized, unless it matches a few patterns that # we don't test unoptimized (or has an OPTIMIZEONLY marker file). if (NOT _testname MATCHES "optix" + AND NOT _testname MATCHES "render" AND NOT EXISTS "${_testsrcdir}/NOSCALAR" AND NOT EXISTS "${_testsrcdir}/BATCHED_REGRESSION" AND NOT EXISTS "${_testsrcdir}/OPTIMIZEONLY") @@ -137,6 +138,7 @@ macro ( TESTSUITE ) # services keeping unoptimized, unless it matches a few patterns that # we don't test unoptimized (or has an OPTIMIZEONLY marker file). if (NOT _testname MATCHES "optix" + AND NOT _testname MATCHES "render" AND NOT EXISTS "${_testsrcdir}/NOSCALAR" AND NOT EXISTS "${_testsrcdir}/BATCHED_REGRESSION" AND NOT EXISTS "${_testsrcdir}/OPTIMIZEONLY" @@ -159,6 +161,7 @@ macro ( TESTSUITE ) # optimization, triggered by setting TESTSHADE_OPT env variable. # Skip OptiX-only tests and those with a NOOPTIMIZE marker file. if (NOT _testname MATCHES "optix" + AND NOT _testname MATCHES "render" AND NOT EXISTS "${_testsrcdir}/NOSCALAR" AND NOT EXISTS "${_testsrcdir}/BATCHED_REGRESSION" AND NOT EXISTS "${_testsrcdir}/NOOPTIMIZE" @@ -350,6 +353,7 @@ macro (osl_add_all_tests) raytype raytype-reg raytype-specialized regex-reg reparam reparam-arrays reparam-string testoptix-reparam render-background render-bumptest + render-bunny render-cornell render-furnace-diffuse render-mx-furnace-burley-diffuse render-mx-furnace-oren-nayar diff --git a/src/testrender/CMakeLists.txt b/src/testrender/CMakeLists.txt index c48808f92..dcd88e0cf 100644 --- a/src/testrender/CMakeLists.txt +++ b/src/testrender/CMakeLists.txt @@ -6,8 +6,12 @@ set (testrender_srcs shading.cpp simpleraytracer.cpp + scene.cpp + bvh.cpp testrender.cpp) +find_package(Threads REQUIRED) + if (OSL_USE_OPTIX) list (APPEND testrender_srcs optixraytracer.cpp) set (testrender_cuda_srcs @@ -69,7 +73,8 @@ add_executable (testrender ${testrender_srcs}) target_link_libraries (testrender PRIVATE oslexec oslquery - pugixml::pugixml) + pugixml::pugixml + Threads::Threads) osl_optix_target (testrender) diff --git a/src/testrender/bvh.cpp b/src/testrender/bvh.cpp new file mode 100644 index 000000000..92eb0e5bb --- /dev/null +++ b/src/testrender/bvh.cpp @@ -0,0 +1,360 @@ +// Copyright Contributors to the Open Shading Language project. +// SPDX-License-Identifier: BSD-3-Clause +// https://github.com/AcademySoftwareFoundation/OpenShadingLanguage + +#include "bvh.h" +#include "raytracer.h" + +#include +#include + +OSL_NAMESPACE_ENTER + +using Box3 = Imath::Box3f; + +struct BuildNode { + Box3 centroid; + unsigned left, right; + unsigned nodeIndex; + int depth; +}; + +float +half_area(const Box3& b) +{ + Vec3 d = b.max - b.min; + return d.x * d.y + d.y * d.z + d.z * d.x; +} + +static constexpr int NumBins = 16; +static constexpr int MaxDepth = 64; + +static std::unique_ptr +build_bvh(OIIO::cspan verts, OIIO::cspan triangles, + OIIO::ErrorHandler& errhandler) +{ + std::unique_ptr bvh = std::make_unique(); + OIIO::Timer timer; + bvh->indices = std::make_unique(triangles.size()); + + std::vector buildnodes; + std::vector triangle_bounds; + buildnodes.reserve(2 * triangles.size() + 1); + buildnodes.emplace_back(); + triangle_bounds.reserve(triangles.size()); + BuildNode current; + Box3 shape_bounds; + for (unsigned i = 0; i < triangles.size(); i++) { + bvh->indices[i] = i; + Vec3 va = verts[triangles[i].a]; + Vec3 vb = verts[triangles[i].b]; + Vec3 vc = verts[triangles[i].c]; + Box3 b(va); + b.extendBy(vb); + b.extendBy(vc); + triangle_bounds.emplace_back(b); + current.centroid.extendBy(b.center()); + shape_bounds.extendBy(b); + } + buildnodes[0].set(shape_bounds.min, shape_bounds.max); + current.left = 0; + current.right = triangles.size(); + current.depth = 1; + current.nodeIndex = 0; + int stackPtr = 0; + BuildNode stack[MaxDepth]; + while (true) { + const unsigned numPrims = current.right - current.left; + if (numPrims > 1 && current.depth < MaxDepth) { + // try to split this set of primitives + Box3 binBounds[3][NumBins]; + unsigned binN[3][NumBins]; + memset(binN, 0, sizeof(binN)); + + float binFactor[3]; + for (int axis = 0; axis < 3; axis++) { + binFactor[axis] = current.centroid.max[axis] + - current.centroid.min[axis]; + binFactor[axis] = (binFactor[axis] > 0) + ? float(0.999f * NumBins) + / binFactor[axis] + : 0; + } + // for each primitive, figure out in which bin it lands per axis + for (unsigned i = current.left; i < current.right; i++) { + unsigned prim = bvh->indices[i]; + Box3 bbox = triangle_bounds[prim]; + Vec3 center = bbox.center(); + for (int axis = 0; axis < 3; axis++) { + int binID = (int)((center[axis] - current.centroid.min[axis]) + * binFactor[axis]); + OSL_ASSERT(binID >= 0 && binID < NumBins); + binN[axis][binID]++; + binBounds[axis][binID].extendBy(bbox); + } + } + // compute the SAH cost of partitioning at each bin + const float invArea = 1 / buildnodes[current.nodeIndex].half_area(); + float bestCost = numPrims; + int bestAxis = -1; + int bestBin = -1; + unsigned bestNL = 0; + unsigned bestNR = 0; + for (int axis = 0; axis < 3; axis++) { + // skip if the current bbox is flat along this axis (splitting would not make sense) + if (binFactor[axis] == 0) + continue; + unsigned countL = 0; + Box3 bbox; + unsigned numL[NumBins]; + float areaL[NumBins]; + for (int i = 0; i < NumBins; i++) { + countL += binN[axis][i]; + numL[i] = countL; + bbox.extendBy(binBounds[axis][i]); + areaL[i] = half_area(bbox); + } + OSL_ASSERT(countL == numPrims); + bbox = binBounds[axis][NumBins - 1]; + for (int i = NumBins - 2; i >= 0; i--) { + if (numL[i] == 0 || numL[i] == numPrims) + continue; // skip if this candidate split does not partition the prims + float areaR = half_area(bbox); + const float trav_cost + = 4; // TODO: tune this if intersection function changes + const float cost = trav_cost + + invArea + * (areaL[i] * numL[i] + + areaR * (numPrims - numL[i])); + if (cost < bestCost) { + bestCost = cost; + bestAxis = axis; + bestBin = i; + bestNL = numL[i]; + bestNR = numPrims - bestNL; + } + bbox.extendBy(binBounds[axis][i]); + } + } + if (bestAxis != -1) { + // split along the found best split + Box3 boundsL, boundsR; + BuildNode bn[2]; + bn[0].depth = bn[1].depth = current.depth + 1; + unsigned rightOrig = current.right; + for (unsigned i = current.left; i < current.right;) { + unsigned prim = bvh->indices[i]; + Box3 bbox = triangle_bounds[prim]; + float center = bbox.center()[bestAxis]; + int binID = (int)((center - current.centroid.min[bestAxis]) + * binFactor[bestAxis]); + OSL_ASSERT(binID >= 0 && binID < NumBins); + if (binID <= bestBin) { + boundsL.extendBy(bbox); + bn[0].centroid.extendBy(bbox.center()); + i++; + } else { + boundsR.extendBy(bbox); + bn[1].centroid.extendBy(bbox.center()); + std::swap(bvh->indices[i], + bvh->indices[--current.right]); + } + } + OSL_ASSERT(bestNL == (current.right - current.left)); + OSL_ASSERT(bestNR == (rightOrig - current.right)); + OSL_ASSERT(bestNL + bestNR == numPrims); + // allocate 2 child nodes + unsigned nextIndex = buildnodes.size(); + buildnodes.emplace_back(); + buildnodes.emplace_back(); + // write to current node + buildnodes[current.nodeIndex].child = nextIndex; + buildnodes[current.nodeIndex].nprims = 0; + bn[0].left = current.left; + bn[0].right = current.right; + bn[1].left = current.right; + bn[1].right = rightOrig; + bn[0].nodeIndex = nextIndex + 0; + bn[1].nodeIndex = nextIndex + 1; + buildnodes[nextIndex + 0].set(boundsL.min, boundsL.max); + buildnodes[nextIndex + 1].set(boundsR.min, boundsR.max); + current = bn[0]; + stack[stackPtr++] = bn[1]; + continue; // keep building + } + } + // nothing more to be done with this node - create a leaf + buildnodes[current.nodeIndex].child = current.left; + buildnodes[current.nodeIndex].nprims = numPrims; + // pop the stack + if (stackPtr == 0) + break; + current = stack[--stackPtr]; + } + bvh->nodes = std::make_unique(buildnodes.size()); + memcpy(bvh->nodes.get(), buildnodes.data(), + buildnodes.size() * sizeof(BVHNode)); + double loadtime = timer(); + errhandler.infofmt("BVH built {} nodes over {} triangles in {}", + buildnodes.size(), triangles.size(), + OIIO::Strutil::timeintervalformat(loadtime, 2)); + errhandler.infofmt("Root bounding box {}, {}, {} to {}, {}, {}", + shape_bounds.min.x, shape_bounds.min.y, + shape_bounds.min.z, shape_bounds.max.x, + shape_bounds.max.y, shape_bounds.max.z); + return bvh; +} + +// min and max, written such that any NaNs in 'b' get ignored +static inline float +minf(float a, float b) +{ + return b < a ? b : a; +} +static inline float +maxf(float a, float b) +{ + return b > a ? b : a; +} + +static inline bool +box_intersect(const Vec3& org, const Vec3& rdir, float tmax, + const float* bounds, float* dist) +{ + const float tx1 = (bounds[0] - org.x) * rdir.x; + const float tx2 = (bounds[1] - org.x) * rdir.x; + const float ty1 = (bounds[2] - org.y) * rdir.y; + const float ty2 = (bounds[3] - org.y) * rdir.y; + const float tz1 = (bounds[4] - org.z) * rdir.z; + const float tz2 = (bounds[5] - org.z) * rdir.z; + float tmin = minf(tx1, tx2); + tmax = minf(tmax, maxf(tx1, tx2)); + tmin = maxf(tmin, minf(ty1, ty2)); + tmax = minf(tmax, maxf(ty1, ty2)); + tmin = maxf(tmin, minf(tz1, tz2)); + tmax = minf(tmax, maxf(tz1, tz2)); + *dist = tmin; // actual distance to near plane on the box + tmin = maxf(0.0f, tmin); // clip to valid portion of ray + return tmin <= tmax; +} + +static inline unsigned +signmask(float a) +{ + return OIIO::bitcast(a) & 0x80000000u; +} +static inline float +xorf(float a, unsigned b) +{ + return OIIO::bitcast(OIIO::bitcast(a) ^ b); +} + +Intersection +Scene::intersect(const Ray& ray, const float tmax, unsigned skipID1, + unsigned skipID2) const +{ + struct StackItem { + BVHNode* node; + float dist; + } stack[MaxDepth]; + Intersection result; + result.t = tmax; + stack[0] = { bvh->nodes.get(), result.t }; + const Vec3 org = ray.origin; + const Vec3 dir = ray.direction; + const Vec3 rdir(1 / dir.x, 1 / dir.y, 1 / dir.z); + int kz = 0; + if (fabsf(dir.y) > fabsf(dir[kz])) + kz = 1; + if (fabsf(dir.z) > fabsf(dir[kz])) + kz = 2; + int kx = kz == 2 ? 0 : kz + 1; + int ky = kx == 2 ? 0 : kx + 1; + const Vec3 shearDir(dir[kx] / dir[kz], dir[ky] / dir[kz], rdir[kz]); + for (int stackPtr = 1; stackPtr != 0;) { + if (result.t < stack[--stackPtr].dist) + continue; + BVHNode* node = stack[stackPtr].node; + if (node->nprims) { + for (unsigned i = 0, nprims = node->nprims; i < nprims; i++) { + unsigned id = bvh->indices[node->child + i]; + // Watertight Ray/Triangle Intersection - JCGT 2013 + // https://jcgt.org/published/0002/01/05/ + const Vec3 A = verts[triangles[id].a] - org; + const Vec3 B = verts[triangles[id].b] - org; + const Vec3 C = verts[triangles[id].c] - org; + const float Ax = A[kx] - shearDir.x * A[kz]; + const float Ay = A[ky] - shearDir.y * A[kz]; + const float Bx = B[kx] - shearDir.x * B[kz]; + const float By = B[ky] - shearDir.y * B[kz]; + const float Cx = C[kx] - shearDir.x * C[kz]; + const float Cy = C[ky] - shearDir.y * C[kz]; + // TODO: could use Kahan's algorithm here + // https://pharr.org/matt/blog/2019/11/03/difference-of-floats + const float U = Cx * By - Cy * Bx; + const float V = Ax * Cy - Ay * Cx; + const float W = Bx * Ay - By * Ax; + if ((U < 0 || V < 0 || W < 0) && (U > 0 || V > 0 || W > 0)) + continue; + const float det = U + V + W; + if (det == 0) + continue; + const float Az = A[kz]; + const float Bz = B[kz]; + const float Cz = C[kz]; + const float T = shearDir.z * (U * Az + V * Bz + W * Cz); + const unsigned mask = signmask(det); + if (xorf(T, mask) < 0) + continue; + if (xorf(T, mask) > result.t * xorf(det, mask)) + continue; + if (id == skipID1) + continue; // skip source triangle + if (id == skipID2) + continue; // skip target triangle + // we know this is a valid hit, record as closest + const float rcpDet = 1 / det; + result.t = T * rcpDet; + result.u = V * rcpDet; + result.v = W * rcpDet; + result.id = id; + } + } else { + BVHNode* child1 = bvh->nodes.get() + node->child; + BVHNode* child2 = child1 + 1; + float dist1 = 0; + bool hit1 = box_intersect(org, rdir, result.t, child1->bounds, + &dist1); + float dist2 = 0; + bool hit2 = box_intersect(org, rdir, result.t, child2->bounds, + &dist2); + if (dist1 > dist2) { + std::swap(hit1, hit2); + std::swap(dist1, dist2); + std::swap(child1, child2); + } + stack[stackPtr] = { child2, dist2 }; + stackPtr += hit2; + stack[stackPtr] = { child1, dist1 }; + stackPtr += hit1; + } + } + return result; +} + +void +Scene::prepare(OIIO::ErrorHandler& errhandler) +{ + verts.shrink_to_fit(); + normals.shrink_to_fit(); + uvs.shrink_to_fit(); + triangles.shrink_to_fit(); + n_triangles.shrink_to_fit(); + uv_triangles.shrink_to_fit(); + OSL_DASSERT(triangles.size() == n_triangles.size()); + OSL_DASSERT(triangles.size() == uv_triangles.size()); + shaderids.shrink_to_fit(); + bvh = build_bvh(verts, triangles, errhandler); +} + +OSL_NAMESPACE_EXIT diff --git a/src/testrender/bvh.h b/src/testrender/bvh.h new file mode 100644 index 000000000..0df14b41e --- /dev/null +++ b/src/testrender/bvh.h @@ -0,0 +1,44 @@ +// Copyright Contributors to the Open Shading Language project. +// SPDX-License-Identifier: BSD-3-Clause +// https://github.com/AcademySoftwareFoundation/OpenShadingLanguage + +#pragma once + +#include +#include + +OSL_NAMESPACE_ENTER + +struct BVHNode { + float bounds[6]; + unsigned child, nprims; + + void set(const Vec3& lo, const Vec3& hi) + { + bounds[0] = lo.x; + bounds[1] = hi.x; + bounds[2] = lo.y; + bounds[3] = hi.y; + bounds[4] = lo.z; + bounds[5] = hi.z; + } + + float half_area() const + { + float vx = bounds[1] - bounds[0]; + float vy = bounds[3] - bounds[2]; + float vz = bounds[5] - bounds[4]; + return vx * vy + vy * vz + vz * vx; + } +}; +struct Intersection { + float t, u, v; + unsigned id; +}; + +struct BVH { + std::unique_ptr nodes; + std::unique_ptr indices; +}; + +OSL_NAMESPACE_EXIT diff --git a/src/testrender/rapidobj/rapidobj.hpp b/src/testrender/rapidobj/rapidobj.hpp new file mode 100755 index 000000000..0d6c88153 --- /dev/null +++ b/src/testrender/rapidobj/rapidobj.hpp @@ -0,0 +1,7706 @@ +/* + +rapidobj - Fast Wavefront .obj file loader + +Licensed under the MIT License +SPDX-License-Identifier: MIT +Copyright (c) 2024 Slobodan Pavlic + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit +persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the +Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef RAPID_OBJ_INCLUDE_HEADER_GUARD +#define RAPID_OBJ_INCLUDE_HEADER_GUARD + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __linux__ + +#include +#include +#include + +#elif _WIN32 + +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX +#include + +#elif __APPLE__ + +#include +#include +#include + +#endif + +#define RAPIDOBJ_VERSION_MAJOR 1 +#define RAPIDOBJ_VERSION_MINOR 1 +#define RAPIDOBJ_VERSION_PATCH 0 + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Public API Begins +// + +namespace rapidobj { + +static constexpr struct { + int Major; + int Minor; + int Patch; +} Version = { RAPIDOBJ_VERSION_MAJOR, RAPIDOBJ_VERSION_MINOR, RAPIDOBJ_VERSION_PATCH }; + +enum class Load { Mandatory, Optional }; + +struct MaterialLibrary final { + static MaterialLibrary Default() { return MaterialLibrary(); } + static MaterialLibrary Default(Load policy) { return MaterialLibrary(policy); } + + static MaterialLibrary SearchPath(std::filesystem::path path, Load policy = Load::Mandatory) + { + return MaterialLibrary({ std::move(path) }, policy); + } + + static MaterialLibrary SearchPaths(std::vector paths, Load policy = Load::Mandatory) + { + return MaterialLibrary(std::move(paths), policy); + } + + static MaterialLibrary String(std::string_view text) { return MaterialLibrary(text); } + + static MaterialLibrary Ignore() { return MaterialLibrary(nullptr); } + + const auto& Value() const noexcept { return m_value; } + const auto& Policy() const noexcept { return m_policy; } + + private: + MaterialLibrary() noexcept = default; + MaterialLibrary(Load policy) noexcept : m_policy(policy) {} + MaterialLibrary(std::vector&& paths, Load policy) noexcept + : m_value(std::move(paths)), m_policy(policy) + {} + MaterialLibrary(std::string_view text) noexcept : m_value(text) {} + MaterialLibrary(std::nullptr_t) noexcept : m_value(nullptr) {} + + using Variant = std::variant, std::string_view>; + + Variant m_value{}; + std::optional m_policy{}; +}; + +template +class Array final { + public: + using value_type = T; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using reference = value_type&; + using const_reference = const value_type&; + using pointer = value_type*; + using const_pointer = const value_type*; + using iterator = pointer; + using const_iterator = const_pointer; + + Array() noexcept {} + Array(size_type size) : m_state{ size ? std::unique_ptr(new T[size]) : nullptr, size } {} + + template + Array(Iter begin, Iter end) + { + static_assert(std::is_same_v); + static_assert(std::is_same_v); + + const auto size = static_cast(end - begin); + m_state.data.reset(new value_type[size]); + memcpy(&*begin, m_state.data.get(), size * sizeof(value_type)); + m_state.size = size; + } + + Array(const Array&) = delete; + Array& operator=(const Array&) = delete; + + Array(Array&& other) noexcept { m_state = std::exchange(other.m_state, {}); } + Array& operator=(Array&& other) noexcept + { + if (this != &other) { + m_state = std::exchange(other.m_state, {}); + } + return *this; + } + + constexpr reference operator[](size_type index) noexcept { return m_state.data[index]; } + constexpr const_reference operator[](size_type index) const noexcept { return m_state.data[index]; } + constexpr reference front() noexcept { return m_state.data[0]; } + constexpr const_reference front() const noexcept { return m_state.data[0]; } + constexpr reference back() noexcept { return m_state.data[m_state.size - 1]; } + constexpr const_reference back() const noexcept { return m_state.data[m_state.size - 1]; } + constexpr pointer data() noexcept { return m_state.data.get(); } + constexpr const_pointer data() const noexcept { return m_state.data.get(); } + constexpr iterator begin() noexcept { return m_state.data.get(); } + constexpr const_iterator begin() const noexcept { return m_state.data.get(); } + constexpr const_iterator cbegin() const noexcept { return m_state.data.get(); } + constexpr iterator end() noexcept { return m_state.data.get() + m_state.size; } + constexpr const_iterator end() const noexcept { return m_state.data.get() + m_state.size; } + constexpr const_iterator cend() const noexcept { return m_state.data.get() + m_state.size; } + constexpr bool empty() const noexcept { return m_state.size == 0; } + constexpr size_type size() const noexcept { return m_state.size; } + void swap(Array& other) noexcept { std::swap(m_state.state, other.m_state.state); } + + private: + struct final { + std::unique_ptr data; + size_type size; + } m_state{}; + + static_assert( + std::is_trivially_constructible_v && std::is_trivially_destructible_v && + std::is_trivially_copyable_v); +}; + +template +Array(Iter b, Iter e) -> Array::value_type>; + +template +bool operator==(const Array& lhs, const Array& rhs) +{ + return (lhs.size() == rhs.size()) && std::equal(lhs.begin(), lhs.end(), rhs.begin()); +} + +template +bool operator!=(const Array& lhs, const Array& rhs) +{ + return !operator==(lhs, rhs); +} + +struct Attributes final { + Array positions; // 'v' (xyz) + Array texcoords; // 'vt' (uv) + Array normals; // 'vn' (xyz) + Array colors; // vertex color extension (see http://paulbourke.net/dataformats/obj/colour.html) +}; + +struct Index final { + int position_index; + int texcoord_index; + int normal_index; +}; + +struct Mesh final { + Array indices; // Position/Texture/Normal indices + Array num_face_vertices; // Number of vertices per face: 3 (triangle), 4 (quad), ... , 255 + Array material_ids; // Material ID per face + Array smoothing_group_ids; // Smoothing group ID per face (group id 0 means off) +}; + +struct Lines final { + Array indices; // Polyline indices + Array num_line_vertices; // Number of vertices per polyline +}; + +struct Points final { + Array indices; // Points indices +}; + +struct Shape final { + std::string name; + Mesh mesh; + Lines lines; + Points points; +}; + +using Shapes = std::vector; + +enum class TextureType { None, Sphere, CubeTop, CubeBottom, CubeFront, CubeBack, CubeLeft, CubeRight }; + +using Float3 = std::array; + +// see https://en.wikipedia.org/wiki/Wavefront_.obj_file#Texture_options +struct TextureOption final { + TextureType type = TextureType::None; // -type + float sharpness = 1.0f; // -boost + float brightness = 0.0f; // -mm + float contrast = 1.0f; // -mm + Float3 origin_offset = { 0, 0, 0 }; // -o + Float3 scale = { 1, 1, 1 }; // -s + Float3 turbulence = { 0, 0, 0 }; // -t + int texture_resolution = -1; // -texres + bool clamp = false; // -clamp + char imfchan = 'm'; // -imfchan + bool blendu = true; // -blendu + bool blendv = true; // -blendv + float bump_multiplier = 1.0f; // -bm +}; + +struct Material final { + std::string name; + + // Material parameters (see http://www.fileformat.info/format/material/) + Float3 ambient = { 0, 0, 0 }; // Ka + Float3 diffuse = { 0, 0, 0 }; // Kd + Float3 specular = { 0, 0, 0 }; // Ks + Float3 transmittance = { 0, 0, 0 }; // Kt + Float3 emission = { 0, 0, 0 }; // Ke + float shininess = 1.0f; // Ns + float ior = 1.0f; // Ni + float dissolve = 1.0f; // d + int illum = 0; // illum + + std::string ambient_texname; // map_Ka + std::string diffuse_texname; // map_Kd + std::string specular_texname; // map_Ks + std::string specular_highlight_texname; // map_Ns + std::string bump_texname; // map_bump, map_Bump, bump + std::string displacement_texname; // disp + std::string alpha_texname; // map_d + std::string reflection_texname; // refl + + TextureOption ambient_texopt; + TextureOption diffuse_texopt; + TextureOption specular_texopt; + TextureOption specular_highlight_texopt; + TextureOption bump_texopt; + TextureOption displacement_texopt; + TextureOption alpha_texopt; + TextureOption reflection_texopt; + + // PBR extension (see http://exocortex.com/blog/extending_wavefront_mtl_to_support_pbr) + float roughness = 0.0f; // Pr + float metallic = 0.0f; // Pm + float sheen = 0.0f; // Ps + float clearcoat_thickness = 0.0f; // Pc + float clearcoat_roughness = 0.0f; // Pcr + float anisotropy = 0.0f; // aniso + float anisotropy_rotation = 0.0f; // anisor + + std::string roughness_texname; // map_Pr + std::string metallic_texname; // map_Pm + std::string sheen_texname; // map_Ps + std::string emissive_texname; // map_Ke + std::string normal_texname; // norm + + TextureOption roughness_texopt; + TextureOption metallic_texopt; + TextureOption sheen_texopt; + TextureOption emissive_texopt; + TextureOption normal_texopt; +}; + +using Materials = std::vector; + +struct Error final { + explicit operator bool() const noexcept { return code.value(); } + + std::error_code code{}; + std::string line{}; + size_t line_num{}; +}; + +struct [[nodiscard]] Result final { + Attributes attributes; + Shapes shapes; + Materials materials; + Error error; +}; + +inline Result ParseFile( + const std::filesystem::path& obj_filepath, + const MaterialLibrary& mtl_library = MaterialLibrary::Default()); + +inline Result ParseStream(std::istream& obj_stream, const MaterialLibrary& mtl_library = MaterialLibrary::Default()); + +inline bool Triangulate(Result& result); + +} // namespace rapidobj + +// +// Public API Ends +// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Third-party includes Begin +// + +// clang-format off + +// +// Code below is from: https://github.com/mapbox/earcut.hpp/tree/v2.2.4 +// + +// ISC License +// +// Copyright (c) 2015, Mapbox +// +// Permission to use, copy, modify, and/or distribute this software for any purpose +// with or without fee is hereby granted, provided that the above copyright notice +// and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +// FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +// OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +// THIS SOFTWARE. + +namespace mapbox { + +namespace util { + +template struct nth { + inline static typename std::tuple_element::type + get(const T& t) { return std::get(t); }; +}; + +} + +namespace detail { + +template +class Earcut { +public: + std::vector indices; + std::size_t vertices = 0; + + template + void operator()(const Polygon& points); + +private: + struct Node { + Node(N index, double x_, double y_) : i(index), x(x_), y(y_) {} + Node(const Node&) = delete; + Node& operator=(const Node&) = delete; + Node(Node&&) = delete; + Node& operator=(Node&&) = delete; + + const N i; + const double x; + const double y; + + // previous and next vertice nodes in a polygon ring + Node* prev = nullptr; + Node* next = nullptr; + + // z-order curve value + int32_t z = 0; + + // previous and next nodes in z-order + Node* prevZ = nullptr; + Node* nextZ = nullptr; + + // indicates whether this is a steiner point + bool steiner = false; + }; + + template Node* linkedList(const Ring& points, const bool clockwise); + Node* filterPoints(Node* start, Node* end = nullptr); + void earcutLinked(Node* ear, int pass = 0); + bool isEar(Node* ear); + bool isEarHashed(Node* ear); + Node* cureLocalIntersections(Node* start); + void splitEarcut(Node* start); + template Node* eliminateHoles(const Polygon& points, Node* outerNode); + Node* eliminateHole(Node* hole, Node* outerNode); + Node* findHoleBridge(Node* hole, Node* outerNode); + bool sectorContainsSector(const Node* m, const Node* p); + void indexCurve(Node* start); + Node* sortLinked(Node* list); + int32_t zOrder(const double x_, const double y_); + Node* getLeftmost(Node* start); + bool pointInTriangle(double ax, double ay, double bx, double by, double cx, double cy, double px, double py) const; + bool isValidDiagonal(Node* a, Node* b); + double area(const Node* p, const Node* q, const Node* r) const; + bool equals(const Node* p1, const Node* p2); + bool intersects(const Node* p1, const Node* q1, const Node* p2, const Node* q2); + bool onSegment(const Node* p, const Node* q, const Node* r); + int sign(double val); + bool intersectsPolygon(const Node* a, const Node* b); + bool locallyInside(const Node* a, const Node* b); + bool middleInside(const Node* a, const Node* b); + Node* splitPolygon(Node* a, Node* b); + template Node* insertNode(std::size_t i, const Point& p, Node* last); + void removeNode(Node* p); + + bool hashing; + double minX, maxX; + double minY, maxY; + double inv_size = 0; + + template > + class ObjectPool { + public: + ObjectPool() { } + ObjectPool(std::size_t blockSize_) { + reset(blockSize_); + } + ~ObjectPool() { + clear(); + } + template + T* construct(Args&&... args) { + if (currentIndex >= blockSize) { + currentBlock = alloc_traits::allocate(alloc, blockSize); + allocations.emplace_back(currentBlock); + currentIndex = 0; + } + T* object = ¤tBlock[currentIndex++]; + alloc_traits::construct(alloc, object, std::forward(args)...); + return object; + } + void reset(std::size_t newBlockSize) { + for (auto allocation : allocations) { + alloc_traits::deallocate(alloc, allocation, blockSize); + } + allocations.clear(); + blockSize = std::max(1, newBlockSize); + currentBlock = nullptr; + currentIndex = blockSize; + } + void clear() { reset(blockSize); } + private: + T* currentBlock = nullptr; + std::size_t currentIndex = 1; + std::size_t blockSize = 1; + std::vector allocations; + Alloc alloc; + typedef typename std::allocator_traits alloc_traits; + }; + ObjectPool nodes; +}; + +template template +void Earcut::operator()(const Polygon& points) { + // reset + indices.clear(); + vertices = 0; + + if (points.empty()) return; + + double x; + double y; + int threshold = 80; + std::size_t len = 0; + + for (size_t i = 0; threshold >= 0 && i < points.size(); i++) { + threshold -= static_cast(points[i].size()); + len += points[i].size(); + } + + //estimate size of nodes and indices + nodes.reset(len * 3 / 2); + indices.reserve(len + points[0].size()); + + Node* outerNode = linkedList(points[0], true); + if (!outerNode || outerNode->prev == outerNode->next) return; + + if (points.size() > 1) outerNode = eliminateHoles(points, outerNode); + + // if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox + hashing = threshold < 0; + if (hashing) { + Node* p = outerNode->next; + minX = maxX = outerNode->x; + minY = maxY = outerNode->y; + do { + x = p->x; + y = p->y; + minX = std::min(minX, x); + minY = std::min(minY, y); + maxX = std::max(maxX, x); + maxY = std::max(maxY, y); + p = p->next; + } while (p != outerNode); + + // minX, minY and inv_size are later used to transform coords into integers for z-order calculation + inv_size = std::max(maxX - minX, maxY - minY); + inv_size = inv_size != .0 ? (32767. / inv_size) : .0; + } + + earcutLinked(outerNode); + + nodes.clear(); +} + +// create a circular doubly linked list from polygon points in the specified winding order +template template +typename Earcut::Node* +Earcut::linkedList(const Ring& points, const bool clockwise) { + using Point = typename Ring::value_type; + double sum = 0; + const std::size_t len = points.size(); + std::size_t i, j; + Node* last = nullptr; + + // calculate original winding order of a polygon ring + for (i = 0, j = len > 0 ? len - 1 : 0; i < len; j = i++) { + const auto& p1 = points[i]; + const auto& p2 = points[j]; + const double p20 = util::nth<0, Point>::get(p2); + const double p10 = util::nth<0, Point>::get(p1); + const double p11 = util::nth<1, Point>::get(p1); + const double p21 = util::nth<1, Point>::get(p2); + sum += (p20 - p10) * (p11 + p21); + } + + // link points into circular doubly-linked list in the specified winding order + if (clockwise == (sum > 0)) { + for (i = 0; i < len; i++) last = insertNode(vertices + i, points[i], last); + } else { + for (i = len; i-- > 0;) last = insertNode(vertices + i, points[i], last); + } + + if (last && equals(last, last->next)) { + removeNode(last); + last = last->next; + } + + vertices += len; + + return last; +} + +// eliminate colinear or duplicate points +template +typename Earcut::Node* +Earcut::filterPoints(Node* start, Node* end) { + if (!end) end = start; + + Node* p = start; + bool again; + do { + again = false; + + if (!p->steiner && (equals(p, p->next) || area(p->prev, p, p->next) == 0)) { + removeNode(p); + p = end = p->prev; + + if (p == p->next) break; + again = true; + + } else { + p = p->next; + } + } while (again || p != end); + + return end; +} + +// main ear slicing loop which triangulates a polygon (given as a linked list) +template +void Earcut::earcutLinked(Node* ear, int pass) { + if (!ear) return; + + // interlink polygon nodes in z-order + if (!pass && hashing) indexCurve(ear); + + Node* stop = ear; + Node* prev; + Node* next; + + // iterate through ears, slicing them one by one + while (ear->prev != ear->next) { + prev = ear->prev; + next = ear->next; + + if (hashing ? isEarHashed(ear) : isEar(ear)) { + // cut off the triangle + indices.emplace_back(prev->i); + indices.emplace_back(ear->i); + indices.emplace_back(next->i); + + removeNode(ear); + + // skipping the next vertice leads to less sliver triangles + ear = next->next; + stop = next->next; + + continue; + } + + ear = next; + + // if we looped through the whole remaining polygon and can't find any more ears + if (ear == stop) { + // try filtering points and slicing again + if (!pass) earcutLinked(filterPoints(ear), 1); + + // if this didn't work, try curing all small self-intersections locally + else if (pass == 1) { + ear = cureLocalIntersections(filterPoints(ear)); + earcutLinked(ear, 2); + + // as a last resort, try splitting the remaining polygon into two + } else if (pass == 2) splitEarcut(ear); + + break; + } + } +} + +// check whether a polygon node forms a valid ear with adjacent nodes +template +bool Earcut::isEar(Node* ear) { + const Node* a = ear->prev; + const Node* b = ear; + const Node* c = ear->next; + + if (area(a, b, c) >= 0) return false; // reflex, can't be an ear + + // now make sure we don't have other points inside the potential ear + Node* p = ear->next->next; + + while (p != ear->prev) { + if (pointInTriangle(a->x, a->y, b->x, b->y, c->x, c->y, p->x, p->y) && + area(p->prev, p, p->next) >= 0) return false; + p = p->next; + } + + return true; +} + +template +bool Earcut::isEarHashed(Node* ear) { + const Node* a = ear->prev; + const Node* b = ear; + const Node* c = ear->next; + + if (area(a, b, c) >= 0) return false; // reflex, can't be an ear + + // triangle bbox; min & max are calculated like this for speed + const double minTX = std::min(a->x, std::min(b->x, c->x)); + const double minTY = std::min(a->y, std::min(b->y, c->y)); + const double maxTX = std::max(a->x, std::max(b->x, c->x)); + const double maxTY = std::max(a->y, std::max(b->y, c->y)); + + // z-order range for the current triangle bbox; + const int32_t minZ = zOrder(minTX, minTY); + const int32_t maxZ = zOrder(maxTX, maxTY); + + // first look for points inside the triangle in increasing z-order + Node* p = ear->nextZ; + + while (p && p->z <= maxZ) { + if (p != ear->prev && p != ear->next && + pointInTriangle(a->x, a->y, b->x, b->y, c->x, c->y, p->x, p->y) && + area(p->prev, p, p->next) >= 0) return false; + p = p->nextZ; + } + + // then look for points in decreasing z-order + p = ear->prevZ; + + while (p && p->z >= minZ) { + if (p != ear->prev && p != ear->next && + pointInTriangle(a->x, a->y, b->x, b->y, c->x, c->y, p->x, p->y) && + area(p->prev, p, p->next) >= 0) return false; + p = p->prevZ; + } + + return true; +} + +// go through all polygon nodes and cure small local self-intersections +template +typename Earcut::Node* +Earcut::cureLocalIntersections(Node* start) { + Node* p = start; + do { + Node* a = p->prev; + Node* b = p->next->next; + + // a self-intersection where edge (v[i-1],v[i]) intersects (v[i+1],v[i+2]) + if (!equals(a, b) && intersects(a, p, p->next, b) && locallyInside(a, b) && locallyInside(b, a)) { + indices.emplace_back(a->i); + indices.emplace_back(p->i); + indices.emplace_back(b->i); + + // remove two nodes involved + removeNode(p); + removeNode(p->next); + + p = start = b; + } + p = p->next; + } while (p != start); + + return filterPoints(p); +} + +// try splitting polygon into two and triangulate them independently +template +void Earcut::splitEarcut(Node* start) { + // look for a valid diagonal that divides the polygon into two + Node* a = start; + do { + Node* b = a->next->next; + while (b != a->prev) { + if (a->i != b->i && isValidDiagonal(a, b)) { + // split the polygon in two by the diagonal + Node* c = splitPolygon(a, b); + + // filter colinear points around the cuts + a = filterPoints(a, a->next); + c = filterPoints(c, c->next); + + // run earcut on each half + earcutLinked(a); + earcutLinked(c); + return; + } + b = b->next; + } + a = a->next; + } while (a != start); +} + +// link every hole into the outer loop, producing a single-ring polygon without holes +template template +typename Earcut::Node* +Earcut::eliminateHoles(const Polygon& points, Node* outerNode) { + const size_t len = points.size(); + + std::vector queue; + for (size_t i = 1; i < len; i++) { + Node* list = linkedList(points[i], false); + if (list) { + if (list == list->next) list->steiner = true; + queue.push_back(getLeftmost(list)); + } + } + std::sort(queue.begin(), queue.end(), [](const Node* a, const Node* b) { + return a->x < b->x; + }); + + // process holes from left to right + for (size_t i = 0; i < queue.size(); i++) { + outerNode = eliminateHole(queue[i], outerNode); + } + + return outerNode; +} + +// find a bridge between vertices that connects hole with an outer ring and and link it +template +typename Earcut::Node* +Earcut::eliminateHole(Node* hole, Node* outerNode) { + Node* bridge = findHoleBridge(hole, outerNode); + if (!bridge) { + return outerNode; + } + + Node* bridgeReverse = splitPolygon(bridge, hole); + + // filter collinear points around the cuts + filterPoints(bridgeReverse, bridgeReverse->next); + + // Check if input node was removed by the filtering + return filterPoints(bridge, bridge->next); +} + +// David Eberly's algorithm for finding a bridge between hole and outer polygon +template +typename Earcut::Node* +Earcut::findHoleBridge(Node* hole, Node* outerNode) { + Node* p = outerNode; + double hx = hole->x; + double hy = hole->y; + double qx = -std::numeric_limits::infinity(); + Node* m = nullptr; + + // find a segment intersected by a ray from the hole's leftmost Vertex to the left; + // segment's endpoint with lesser x will be potential connection Vertex + do { + if (hy <= p->y && hy >= p->next->y && p->next->y != p->y) { + double x = p->x + (hy - p->y) * (p->next->x - p->x) / (p->next->y - p->y); + if (x <= hx && x > qx) { + qx = x; + m = p->x < p->next->x ? p : p->next; + if (x == hx) return m; // hole touches outer segment; pick leftmost endpoint + } + } + p = p->next; + } while (p != outerNode); + + if (!m) return 0; + + // look for points inside the triangle of hole Vertex, segment intersection and endpoint; + // if there are no points found, we have a valid connection; + // otherwise choose the Vertex of the minimum angle with the ray as connection Vertex + + const Node* stop = m; + double tanMin = std::numeric_limits::infinity(); + double tanCur = 0; + + p = m; + double mx = m->x; + double my = m->y; + + do { + if (hx >= p->x && p->x >= mx && hx != p->x && + pointInTriangle(hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p->x, p->y)) { + + tanCur = std::abs(hy - p->y) / (hx - p->x); // tangential + + if (locallyInside(p, hole) && + (tanCur < tanMin || (tanCur == tanMin && (p->x > m->x || sectorContainsSector(m, p))))) { + m = p; + tanMin = tanCur; + } + } + + p = p->next; + } while (p != stop); + + return m; +} + +// whether sector in vertex m contains sector in vertex p in the same coordinates +template +bool Earcut::sectorContainsSector(const Node* m, const Node* p) { + return area(m->prev, m, p->prev) < 0 && area(p->next, m, m->next) < 0; +} + +// interlink polygon nodes in z-order +template +void Earcut::indexCurve(Node* start) { + assert(start); + Node* p = start; + + do { + p->z = p->z ? p->z : zOrder(p->x, p->y); + p->prevZ = p->prev; + p->nextZ = p->next; + p = p->next; + } while (p != start); + + p->prevZ->nextZ = nullptr; + p->prevZ = nullptr; + + sortLinked(p); +} + +// Simon Tatham's linked list merge sort algorithm +// http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html +template +typename Earcut::Node* +Earcut::sortLinked(Node* list) { + assert(list); + Node* p; + Node* q; + Node* e; + Node* tail; + int i, numMerges, pSize, qSize; + int inSize = 1; + + for (;;) { + p = list; + list = nullptr; + tail = nullptr; + numMerges = 0; + + while (p) { + numMerges++; + q = p; + pSize = 0; + for (i = 0; i < inSize; i++) { + pSize++; + q = q->nextZ; + if (!q) break; + } + + qSize = inSize; + + while (pSize > 0 || (qSize > 0 && q)) { + + if (pSize == 0) { + e = q; + q = q->nextZ; + qSize--; + } else if (qSize == 0 || !q) { + e = p; + p = p->nextZ; + pSize--; + } else if (p->z <= q->z) { + e = p; + p = p->nextZ; + pSize--; + } else { + e = q; + q = q->nextZ; + qSize--; + } + + if (tail) tail->nextZ = e; + else list = e; + + e->prevZ = tail; + tail = e; + } + + p = q; + } + + tail->nextZ = nullptr; + + if (numMerges <= 1) return list; + + inSize *= 2; + } +} + +// z-order of a Vertex given coords and size of the data bounding box +template +int32_t Earcut::zOrder(const double x_, const double y_) { + // coords are transformed into non-negative 15-bit integer range + int32_t x = static_cast((x_ - minX) * inv_size); + int32_t y = static_cast((y_ - minY) * inv_size); + + x = (x | (x << 8)) & 0x00FF00FF; + x = (x | (x << 4)) & 0x0F0F0F0F; + x = (x | (x << 2)) & 0x33333333; + x = (x | (x << 1)) & 0x55555555; + + y = (y | (y << 8)) & 0x00FF00FF; + y = (y | (y << 4)) & 0x0F0F0F0F; + y = (y | (y << 2)) & 0x33333333; + y = (y | (y << 1)) & 0x55555555; + + return x | (y << 1); +} + +// find the leftmost node of a polygon ring +template +typename Earcut::Node* +Earcut::getLeftmost(Node* start) { + Node* p = start; + Node* leftmost = start; + do { + if (p->x < leftmost->x || (p->x == leftmost->x && p->y < leftmost->y)) + leftmost = p; + p = p->next; + } while (p != start); + + return leftmost; +} + +// check if a point lies within a convex triangle +template +bool Earcut::pointInTriangle(double ax, double ay, double bx, double by, double cx, double cy, double px, double py) const { + return (cx - px) * (ay - py) >= (ax - px) * (cy - py) && + (ax - px) * (by - py) >= (bx - px) * (ay - py) && + (bx - px) * (cy - py) >= (cx - px) * (by - py); +} + +// check if a diagonal between two polygon nodes is valid (lies in polygon interior) +template +bool Earcut::isValidDiagonal(Node* a, Node* b) { + return a->next->i != b->i && a->prev->i != b->i && !intersectsPolygon(a, b) && // dones't intersect other edges + ((locallyInside(a, b) && locallyInside(b, a) && middleInside(a, b) && // locally visible + (area(a->prev, a, b->prev) != 0.0 || area(a, b->prev, b) != 0.0)) || // does not create opposite-facing sectors + (equals(a, b) && area(a->prev, a, a->next) > 0 && area(b->prev, b, b->next) > 0)); // special zero-length case +} + +// signed area of a triangle +template +double Earcut::area(const Node* p, const Node* q, const Node* r) const { + return (q->y - p->y) * (r->x - q->x) - (q->x - p->x) * (r->y - q->y); +} + +// check if two points are equal +template +bool Earcut::equals(const Node* p1, const Node* p2) { + return p1->x == p2->x && p1->y == p2->y; +} + +// check if two segments intersect +template +bool Earcut::intersects(const Node* p1, const Node* q1, const Node* p2, const Node* q2) { + int o1 = sign(area(p1, q1, p2)); + int o2 = sign(area(p1, q1, q2)); + int o3 = sign(area(p2, q2, p1)); + int o4 = sign(area(p2, q2, q1)); + + if (o1 != o2 && o3 != o4) return true; // general case + + if (o1 == 0 && onSegment(p1, p2, q1)) return true; // p1, q1 and p2 are collinear and p2 lies on p1q1 + if (o2 == 0 && onSegment(p1, q2, q1)) return true; // p1, q1 and q2 are collinear and q2 lies on p1q1 + if (o3 == 0 && onSegment(p2, p1, q2)) return true; // p2, q2 and p1 are collinear and p1 lies on p2q2 + if (o4 == 0 && onSegment(p2, q1, q2)) return true; // p2, q2 and q1 are collinear and q1 lies on p2q2 + + return false; +} + +// for collinear points p, q, r, check if point q lies on segment pr +template +bool Earcut::onSegment(const Node* p, const Node* q, const Node* r) { + return q->x <= std::max(p->x, r->x) && + q->x >= std::min(p->x, r->x) && + q->y <= std::max(p->y, r->y) && + q->y >= std::min(p->y, r->y); +} + +template +int Earcut::sign(double val) { + return (0.0 < val) - (val < 0.0); +} + +// check if a polygon diagonal intersects any polygon segments +template +bool Earcut::intersectsPolygon(const Node* a, const Node* b) { + const Node* p = a; + do { + if (p->i != a->i && p->next->i != a->i && p->i != b->i && p->next->i != b->i && + intersects(p, p->next, a, b)) return true; + p = p->next; + } while (p != a); + + return false; +} + +// check if a polygon diagonal is locally inside the polygon +template +bool Earcut::locallyInside(const Node* a, const Node* b) { + return area(a->prev, a, a->next) < 0 ? + area(a, b, a->next) >= 0 && area(a, a->prev, b) >= 0 : + area(a, b, a->prev) < 0 || area(a, a->next, b) < 0; +} + +// check if the middle Vertex of a polygon diagonal is inside the polygon +template +bool Earcut::middleInside(const Node* a, const Node* b) { + const Node* p = a; + bool inside = false; + double px = (a->x + b->x) / 2; + double py = (a->y + b->y) / 2; + do { + if (((p->y > py) != (p->next->y > py)) && p->next->y != p->y && + (px < (p->next->x - p->x) * (py - p->y) / (p->next->y - p->y) + p->x)) + inside = !inside; + p = p->next; + } while (p != a); + + return inside; +} + +// link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits +// polygon into two; if one belongs to the outer ring and another to a hole, it merges it into a +// single ring +template +typename Earcut::Node* +Earcut::splitPolygon(Node* a, Node* b) { + Node* a2 = nodes.construct(a->i, a->x, a->y); + Node* b2 = nodes.construct(b->i, b->x, b->y); + Node* an = a->next; + Node* bp = b->prev; + + a->next = b; + b->prev = a; + + a2->next = an; + an->prev = a2; + + b2->next = a2; + a2->prev = b2; + + bp->next = b2; + b2->prev = bp; + + return b2; +} + +// create a node and util::optionally link it with previous one (in a circular doubly linked list) +template template +typename Earcut::Node* +Earcut::insertNode(std::size_t i, const Point& pt, Node* last) { + Node* p = nodes.construct(static_cast(i), util::nth<0, Point>::get(pt), util::nth<1, Point>::get(pt)); + + if (!last) { + p->prev = p; + p->next = p; + + } else { + assert(last); + p->next = last->next; + p->prev = last; + last->next->prev = p; + last->next = p; + } + return p; +} + +template +void Earcut::removeNode(Node* p) { + p->next->prev = p->prev; + p->prev->next = p->next; + + if (p->prevZ) p->prevZ->nextZ = p->nextZ; + if (p->nextZ) p->nextZ->prevZ = p->prevZ; +} +} + +template +std::vector earcut(const Polygon& poly) { + mapbox::detail::Earcut earcut; + earcut(poly); + return std::move(earcut.indices); +} +} + +// +// Code below is from: https://github.com/fastfloat/fast_float/releases/download/v3.5.1/fast_float.h +// + +// fast_float by Daniel Lemire +// fast_float by João Paulo Magalhaes +// +// with contributions from Eugene Golushkov +// with contributions from Maksim Kita +// with contributions from Marcin Wojdyr +// with contributions from Neal Richardson +// with contributions from Tim Paine +// with contributions from Fabio Pellacini +// +// Licensed under the Apache License, Version 2.0, or the +// MIT License at your option. This file may not be copied, +// modified, or distributed except according to those terms. +// +// MIT License Notice +// +// MIT License +// +// Copyright (c) 2021 The fast_float authors +// +// Permission is hereby granted, free of charge, to any +// person obtaining a copy of this software and associated +// documentation files (the "Software"), to deal in the +// Software without restriction, including without +// limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice +// shall be included in all copies or substantial portions +// of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// Apache License (Version 2.0) Notice +// +// Copyright 2021 The fast_float authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// + +#ifndef FASTFLOAT_FAST_FLOAT_H +#define FASTFLOAT_FAST_FLOAT_H + +namespace fast_float { +enum chars_format { + scientific = 1<<0, + fixed = 1<<2, + hex = 1<<3, + general = fixed | scientific +}; + + +struct from_chars_result { + const char *ptr; + std::errc ec; +}; + +struct parse_options { + constexpr explicit parse_options(chars_format fmt = chars_format::general, + char dot = '.') + : format(fmt), decimal_point(dot) {} + + /** Which number formats are accepted */ + chars_format format; + /** The character used as decimal point */ + char decimal_point; +}; + +/** + * This function parses the character sequence [first,last) for a number. It parses floating-point numbers expecting + * a locale-indepent format equivalent to what is used by std::strtod in the default ("C") locale. + * The resulting floating-point value is the closest floating-point values (using either float or double), + * using the "round to even" convention for values that would otherwise fall right in-between two values. + * That is, we provide exact parsing according to the IEEE standard. + * + * Given a successful parse, the pointer (`ptr`) in the returned value is set to point right after the + * parsed number, and the `value` referenced is set to the parsed value. In case of error, the returned + * `ec` contains a representative error, otherwise the default (`std::errc()`) value is stored. + * + * The implementation does not throw and does not allocate memory (e.g., with `new` or `malloc`). + * + * Like the C++17 standard, the `fast_float::from_chars` functions take an optional last argument of + * the type `fast_float::chars_format`. It is a bitset value: we check whether + * `fmt & fast_float::chars_format::fixed` and `fmt & fast_float::chars_format::scientific` are set + * to determine whether we allowe the fixed point and scientific notation respectively. + * The default is `fast_float::chars_format::general` which allows both `fixed` and `scientific`. + */ +template +from_chars_result from_chars(const char *first, const char *last, + T &value, chars_format fmt = chars_format::general) noexcept; + +/** + * Like from_chars, but accepts an `options` argument to govern number parsing. + */ +template +from_chars_result from_chars_advanced(const char *first, const char *last, + T &value, parse_options options) noexcept; + +} +#endif // FASTFLOAT_FAST_FLOAT_H + +#ifndef FASTFLOAT_FLOAT_COMMON_H +#define FASTFLOAT_FLOAT_COMMON_H + +#if (defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) \ + || defined(__amd64) || defined(__aarch64__) || defined(_M_ARM64) \ + || defined(__MINGW64__) \ + || defined(__s390x__) \ + || (defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || defined(__PPC64LE__)) ) +#define FASTFLOAT_64BIT +#elif (defined(__i386) || defined(__i386__) || defined(_M_IX86) \ + || defined(__arm__) || defined(_M_ARM) \ + || defined(__MINGW32__) || defined(__EMSCRIPTEN__)) +#define FASTFLOAT_32BIT +#else + // Need to check incrementally, since SIZE_MAX is a size_t, avoid overflow. + // We can never tell the register width, but the SIZE_MAX is a good approximation. + // UINTPTR_MAX and INTPTR_MAX are optional, so avoid them for max portability. + #if SIZE_MAX == 0xffff + #error Unknown platform (16-bit, unsupported) + #elif SIZE_MAX == 0xffffffff + #define FASTFLOAT_32BIT + #elif SIZE_MAX == 0xffffffffffffffff + #define FASTFLOAT_64BIT + #else + #error Unknown platform (not 32-bit, not 64-bit?) + #endif +#endif + +#if ((defined(_WIN32) || defined(_WIN64)) && !defined(__clang__)) +#include +#endif + +#if defined(_MSC_VER) && !defined(__clang__) +#define FASTFLOAT_VISUAL_STUDIO 1 +#endif + +#if defined __BYTE_ORDER__ && defined __ORDER_BIG_ENDIAN__ +#define FASTFLOAT_IS_BIG_ENDIAN (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) +#elif defined _WIN32 +#define FASTFLOAT_IS_BIG_ENDIAN 0 +#else +#if defined(__APPLE__) || defined(__FreeBSD__) +#include +#elif defined(sun) || defined(__sun) +#include +#else +#include +#endif +# +#ifndef __BYTE_ORDER__ +// safe choice +#define FASTFLOAT_IS_BIG_ENDIAN 0 +#endif +# +#ifndef __ORDER_LITTLE_ENDIAN__ +// safe choice +#define FASTFLOAT_IS_BIG_ENDIAN 0 +#endif +# +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +#define FASTFLOAT_IS_BIG_ENDIAN 0 +#else +#define FASTFLOAT_IS_BIG_ENDIAN 1 +#endif +#endif + +#ifdef FASTFLOAT_VISUAL_STUDIO +#define fastfloat_really_inline __forceinline +#else +#define fastfloat_really_inline inline __attribute__((always_inline)) +#endif + +#ifndef FASTFLOAT_ASSERT +#define FASTFLOAT_ASSERT(x) { if (!(x)) abort(); } +#endif + +#ifndef FASTFLOAT_DEBUG_ASSERT +#define FASTFLOAT_DEBUG_ASSERT(x) assert(x) +#endif + +// rust style `try!()` macro, or `?` operator +#define FASTFLOAT_TRY(x) { if (!(x)) return false; } + +namespace fast_float { + +// Compares two ASCII strings in a case insensitive manner. +inline bool fastfloat_strncasecmp(const char *input1, const char *input2, + size_t length) { + char running_diff{0}; + for (size_t i = 0; i < length; i++) { + running_diff |= (input1[i] ^ input2[i]); + } + return (running_diff == 0) || (running_diff == 32); +} + +#ifndef FLT_EVAL_METHOD +#error "FLT_EVAL_METHOD should be defined, please include cfloat." +#endif + +// a pointer and a length to a contiguous block of memory +template +struct span { + const T* ptr; + size_t length; + span(const T* _ptr, size_t _length) : ptr(_ptr), length(_length) {} + span() : ptr(nullptr), length(0) {} + + constexpr size_t len() const noexcept { + return length; + } + + const T& operator[](size_t index) const noexcept { + FASTFLOAT_DEBUG_ASSERT(index < length); + return ptr[index]; + } +}; + +struct value128 { + uint64_t low; + uint64_t high; + value128(uint64_t _low, uint64_t _high) : low(_low), high(_high) {} + value128() : low(0), high(0) {} +}; + +/* result might be undefined when input_num is zero */ +fastfloat_really_inline int leading_zeroes(uint64_t input_num) { + assert(input_num > 0); +#ifdef FASTFLOAT_VISUAL_STUDIO + #if defined(_M_X64) || defined(_M_ARM64) + unsigned long leading_zero = 0; + // Search the mask data from most significant bit (MSB) + // to least significant bit (LSB) for a set bit (1). + _BitScanReverse64(&leading_zero, input_num); + return (int)(63 - leading_zero); + #else + int last_bit = 0; + if(input_num & uint64_t(0xffffffff00000000)) input_num >>= 32, last_bit |= 32; + if(input_num & uint64_t( 0xffff0000)) input_num >>= 16, last_bit |= 16; + if(input_num & uint64_t( 0xff00)) input_num >>= 8, last_bit |= 8; + if(input_num & uint64_t( 0xf0)) input_num >>= 4, last_bit |= 4; + if(input_num & uint64_t( 0xc)) input_num >>= 2, last_bit |= 2; + if(input_num & uint64_t( 0x2)) input_num >>= 1, last_bit |= 1; + return 63 - last_bit; + #endif +#else + return __builtin_clzll(input_num); +#endif +} + +#ifdef FASTFLOAT_32BIT + +// slow emulation routine for 32-bit +fastfloat_really_inline uint64_t emulu(uint32_t x, uint32_t y) { + return x * (uint64_t)y; +} + +// slow emulation routine for 32-bit +#if !defined(__MINGW64__) +fastfloat_really_inline uint64_t _umul128(uint64_t ab, uint64_t cd, + uint64_t *hi) { + uint64_t ad = emulu((uint32_t)(ab >> 32), (uint32_t)cd); + uint64_t bd = emulu((uint32_t)ab, (uint32_t)cd); + uint64_t adbc = ad + emulu((uint32_t)ab, (uint32_t)(cd >> 32)); + uint64_t adbc_carry = !!(adbc < ad); + uint64_t lo = bd + (adbc << 32); + *hi = emulu((uint32_t)(ab >> 32), (uint32_t)(cd >> 32)) + (adbc >> 32) + + (adbc_carry << 32) + !!(lo < bd); + return lo; +} +#endif // !__MINGW64__ + +#endif // FASTFLOAT_32BIT + + +// compute 64-bit a*b +fastfloat_really_inline value128 full_multiplication(uint64_t a, + uint64_t b) { + value128 answer; +#ifdef _M_ARM64 + // ARM64 has native support for 64-bit multiplications, no need to emulate + answer.high = __umulh(a, b); + answer.low = a * b; +#elif defined(FASTFLOAT_32BIT) || (defined(_WIN64) && !defined(__clang__)) + answer.low = _umul128(a, b, &answer.high); // _umul128 not available on ARM64 +#elif defined(FASTFLOAT_64BIT) + __uint128_t r = ((__uint128_t)a) * b; + answer.low = uint64_t(r); + answer.high = uint64_t(r >> 64); +#else + #error Not implemented +#endif + return answer; +} + +struct adjusted_mantissa { + uint64_t mantissa{0}; + int32_t power2{0}; // a negative value indicates an invalid result + adjusted_mantissa() = default; + bool operator==(const adjusted_mantissa &o) const { + return mantissa == o.mantissa && power2 == o.power2; + } + bool operator!=(const adjusted_mantissa &o) const { + return mantissa != o.mantissa || power2 != o.power2; + } +}; + +// Bias so we can get the real exponent with an invalid adjusted_mantissa. +constexpr static int32_t invalid_am_bias = -0x8000; + +constexpr static double powers_of_ten_double[] = { + 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, + 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22}; +constexpr static float powers_of_ten_float[] = {1e0, 1e1, 1e2, 1e3, 1e4, 1e5, + 1e6, 1e7, 1e8, 1e9, 1e10}; + +template struct binary_format { + using equiv_uint = typename std::conditional::type; + + static inline constexpr int mantissa_explicit_bits(); + static inline constexpr int minimum_exponent(); + static inline constexpr int infinite_power(); + static inline constexpr int sign_index(); + static inline constexpr int min_exponent_fast_path(); + static inline constexpr int max_exponent_fast_path(); + static inline constexpr int max_exponent_round_to_even(); + static inline constexpr int min_exponent_round_to_even(); + static inline constexpr uint64_t max_mantissa_fast_path(); + static inline constexpr int largest_power_of_ten(); + static inline constexpr int smallest_power_of_ten(); + static inline constexpr T exact_power_of_ten(int64_t power); + static inline constexpr size_t max_digits(); + static inline constexpr equiv_uint exponent_mask(); + static inline constexpr equiv_uint mantissa_mask(); + static inline constexpr equiv_uint hidden_bit_mask(); +}; + +template <> inline constexpr int binary_format::mantissa_explicit_bits() { + return 52; +} +template <> inline constexpr int binary_format::mantissa_explicit_bits() { + return 23; +} + +template <> inline constexpr int binary_format::max_exponent_round_to_even() { + return 23; +} + +template <> inline constexpr int binary_format::max_exponent_round_to_even() { + return 10; +} + +template <> inline constexpr int binary_format::min_exponent_round_to_even() { + return -4; +} + +template <> inline constexpr int binary_format::min_exponent_round_to_even() { + return -17; +} + +template <> inline constexpr int binary_format::minimum_exponent() { + return -1023; +} +template <> inline constexpr int binary_format::minimum_exponent() { + return -127; +} + +template <> inline constexpr int binary_format::infinite_power() { + return 0x7FF; +} +template <> inline constexpr int binary_format::infinite_power() { + return 0xFF; +} + +template <> inline constexpr int binary_format::sign_index() { return 63; } +template <> inline constexpr int binary_format::sign_index() { return 31; } + +template <> inline constexpr int binary_format::min_exponent_fast_path() { +#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0) + return 0; +#else + return -22; +#endif +} +template <> inline constexpr int binary_format::min_exponent_fast_path() { +#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0) + return 0; +#else + return -10; +#endif +} + +template <> inline constexpr int binary_format::max_exponent_fast_path() { + return 22; +} +template <> inline constexpr int binary_format::max_exponent_fast_path() { + return 10; +} + +template <> inline constexpr uint64_t binary_format::max_mantissa_fast_path() { + return uint64_t(2) << mantissa_explicit_bits(); +} +template <> inline constexpr uint64_t binary_format::max_mantissa_fast_path() { + return uint64_t(2) << mantissa_explicit_bits(); +} + +template <> +inline constexpr double binary_format::exact_power_of_ten(int64_t power) { + return powers_of_ten_double[power]; +} +template <> +inline constexpr float binary_format::exact_power_of_ten(int64_t power) { + + return powers_of_ten_float[power]; +} + + +template <> +inline constexpr int binary_format::largest_power_of_ten() { + return 308; +} +template <> +inline constexpr int binary_format::largest_power_of_ten() { + return 38; +} + +template <> +inline constexpr int binary_format::smallest_power_of_ten() { + return -342; +} +template <> +inline constexpr int binary_format::smallest_power_of_ten() { + return -65; +} + +template <> inline constexpr size_t binary_format::max_digits() { + return 769; +} +template <> inline constexpr size_t binary_format::max_digits() { + return 114; +} + +template <> inline constexpr binary_format::equiv_uint + binary_format::exponent_mask() { + return 0x7F800000; +} +template <> inline constexpr binary_format::equiv_uint + binary_format::exponent_mask() { + return 0x7FF0000000000000; +} + +template <> inline constexpr binary_format::equiv_uint + binary_format::mantissa_mask() { + return 0x007FFFFF; +} +template <> inline constexpr binary_format::equiv_uint + binary_format::mantissa_mask() { + return 0x000FFFFFFFFFFFFF; +} + +template <> inline constexpr binary_format::equiv_uint + binary_format::hidden_bit_mask() { + return 0x00800000; +} +template <> inline constexpr binary_format::equiv_uint + binary_format::hidden_bit_mask() { + return 0x0010000000000000; +} + +template +fastfloat_really_inline void to_float(bool negative, adjusted_mantissa am, T &value) { + uint64_t word = am.mantissa; + word |= uint64_t(am.power2) << binary_format::mantissa_explicit_bits(); + word = negative + ? word | (uint64_t(1) << binary_format::sign_index()) : word; +#if FASTFLOAT_IS_BIG_ENDIAN == 1 + if (std::is_same::value) { + ::memcpy(&value, (char *)&word + 4, sizeof(T)); // extract value at offset 4-7 if float on big-endian + } else { + ::memcpy(&value, &word, sizeof(T)); + } +#else + // For little-endian systems: + ::memcpy(&value, &word, sizeof(T)); +#endif +} + +} // namespace fast_float + +#endif + +#ifndef FASTFLOAT_ASCII_NUMBER_H +#define FASTFLOAT_ASCII_NUMBER_H + +namespace fast_float { + +// Next function can be micro-optimized, but compilers are entirely +// able to optimize it well. +fastfloat_really_inline bool is_integer(char c) noexcept { return c >= '0' && c <= '9'; } + +fastfloat_really_inline uint64_t byteswap(uint64_t val) { + return (val & 0xFF00000000000000) >> 56 + | (val & 0x00FF000000000000) >> 40 + | (val & 0x0000FF0000000000) >> 24 + | (val & 0x000000FF00000000) >> 8 + | (val & 0x00000000FF000000) << 8 + | (val & 0x0000000000FF0000) << 24 + | (val & 0x000000000000FF00) << 40 + | (val & 0x00000000000000FF) << 56; +} + +fastfloat_really_inline uint64_t read_u64(const char *chars) { + uint64_t val; + ::memcpy(&val, chars, sizeof(uint64_t)); +#if FASTFLOAT_IS_BIG_ENDIAN == 1 + // Need to read as-if the number was in little-endian order. + val = byteswap(val); +#endif + return val; +} + +fastfloat_really_inline void write_u64(uint8_t *chars, uint64_t val) { +#if FASTFLOAT_IS_BIG_ENDIAN == 1 + // Need to read as-if the number was in little-endian order. + val = byteswap(val); +#endif + ::memcpy(chars, &val, sizeof(uint64_t)); +} + +// credit @aqrit +fastfloat_really_inline uint32_t parse_eight_digits_unrolled(uint64_t val) { + const uint64_t mask = 0x000000FF000000FF; + const uint64_t mul1 = 0x000F424000000064; // 100 + (1000000ULL << 32) + const uint64_t mul2 = 0x0000271000000001; // 1 + (10000ULL << 32) + val -= 0x3030303030303030; + val = (val * 10) + (val >> 8); // val = (val * 2561) >> 8; + val = (((val & mask) * mul1) + (((val >> 16) & mask) * mul2)) >> 32; + return uint32_t(val); +} + +fastfloat_really_inline uint32_t parse_eight_digits_unrolled(const char *chars) noexcept { + return parse_eight_digits_unrolled(read_u64(chars)); +} + +// credit @aqrit +fastfloat_really_inline bool is_made_of_eight_digits_fast(uint64_t val) noexcept { + return !((((val + 0x4646464646464646) | (val - 0x3030303030303030)) & + 0x8080808080808080)); +} + +fastfloat_really_inline bool is_made_of_eight_digits_fast(const char *chars) noexcept { + return is_made_of_eight_digits_fast(read_u64(chars)); +} + +typedef span byte_span; + +struct parsed_number_string { + int64_t exponent{0}; + uint64_t mantissa{0}; + const char *lastmatch{nullptr}; + bool negative{false}; + bool valid{false}; + bool too_many_digits{false}; + // contains the range of the significant digits + byte_span integer{}; // non-nullable + byte_span fraction{}; // nullable +}; + +// Assuming that you use no more than 19 digits, this will +// parse an ASCII string. +fastfloat_really_inline +parsed_number_string parse_number_string(const char *p, const char *pend, parse_options options) noexcept { + const chars_format fmt = options.format; + const char decimal_point = options.decimal_point; + + parsed_number_string answer; + answer.valid = false; + answer.too_many_digits = false; + answer.negative = (*p == '-'); + if (*p == '-') { // C++17 20.19.3.(7.1) explicitly forbids '+' sign here + ++p; + if (p == pend) { + return answer; + } + if (!is_integer(*p) && (*p != decimal_point)) { // a sign must be followed by an integer or the dot + return answer; + } + } + const char *const start_digits = p; + + uint64_t i = 0; // an unsigned int avoids signed overflows (which are bad) + + while ((std::distance(p, pend) >= 8) && is_made_of_eight_digits_fast(p)) { + i = i * 100000000 + parse_eight_digits_unrolled(p); // in rare cases, this will overflow, but that's ok + p += 8; + } + while ((p != pend) && is_integer(*p)) { + // a multiplication by 10 is cheaper than an arbitrary integer + // multiplication + i = 10 * i + + uint64_t(*p - '0'); // might overflow, we will handle the overflow later + ++p; + } + const char *const end_of_integer_part = p; + int64_t digit_count = int64_t(end_of_integer_part - start_digits); + answer.integer = byte_span(start_digits, size_t(digit_count)); + int64_t exponent = 0; + if ((p != pend) && (*p == decimal_point)) { + ++p; + const char* before = p; + // can occur at most twice without overflowing, but let it occur more, since + // for integers with many digits, digit parsing is the primary bottleneck. + while ((std::distance(p, pend) >= 8) && is_made_of_eight_digits_fast(p)) { + i = i * 100000000 + parse_eight_digits_unrolled(p); // in rare cases, this will overflow, but that's ok + p += 8; + } + while ((p != pend) && is_integer(*p)) { + uint8_t digit = uint8_t(*p - '0'); + ++p; + i = i * 10 + digit; // in rare cases, this will overflow, but that's ok + } + exponent = before - p; + answer.fraction = byte_span(before, size_t(p - before)); + digit_count -= exponent; + } + // we must have encountered at least one integer! + if (digit_count == 0) { + return answer; + } + int64_t exp_number = 0; // explicit exponential part + if ((fmt & chars_format::scientific) && (p != pend) && (('e' == *p) || ('E' == *p))) { + const char * location_of_e = p; + ++p; + bool neg_exp = false; + if ((p != pend) && ('-' == *p)) { + neg_exp = true; + ++p; + } else if ((p != pend) && ('+' == *p)) { // '+' on exponent is allowed by C++17 20.19.3.(7.1) + ++p; + } + if ((p == pend) || !is_integer(*p)) { + if(!(fmt & chars_format::fixed)) { + // We are in error. + return answer; + } + // Otherwise, we will be ignoring the 'e'. + p = location_of_e; + } else { + while ((p != pend) && is_integer(*p)) { + uint8_t digit = uint8_t(*p - '0'); + if (exp_number < 0x10000000) { + exp_number = 10 * exp_number + digit; + } + ++p; + } + if(neg_exp) { exp_number = - exp_number; } + exponent += exp_number; + } + } else { + // If it scientific and not fixed, we have to bail out. + if((fmt & chars_format::scientific) && !(fmt & chars_format::fixed)) { return answer; } + } + answer.lastmatch = p; + answer.valid = true; + + // If we frequently had to deal with long strings of digits, + // we could extend our code by using a 128-bit integer instead + // of a 64-bit integer. However, this is uncommon. + // + // We can deal with up to 19 digits. + if (digit_count > 19) { // this is uncommon + // It is possible that the integer had an overflow. + // We have to handle the case where we have 0.0000somenumber. + // We need to be mindful of the case where we only have zeroes... + // E.g., 0.000000000...000. + const char *start = start_digits; + while ((start != pend) && (*start == '0' || *start == decimal_point)) { + if(*start == '0') { digit_count --; } + start++; + } + if (digit_count > 19) { + answer.too_many_digits = true; + // Let us start again, this time, avoiding overflows. + // We don't need to check if is_integer, since we use the + // pre-tokenized spans from above. + i = 0; + p = answer.integer.ptr; + const char* int_end = p + answer.integer.len(); + const uint64_t minimal_nineteen_digit_integer{1000000000000000000}; + while((i < minimal_nineteen_digit_integer) && (p != int_end)) { + i = i * 10 + uint64_t(*p - '0'); + ++p; + } + if (i >= minimal_nineteen_digit_integer) { // We have a big integers + exponent = end_of_integer_part - p + exp_number; + } else { // We have a value with a fractional component. + p = answer.fraction.ptr; + const char* frac_end = p + answer.fraction.len(); + while((i < minimal_nineteen_digit_integer) && (p != frac_end)) { + i = i * 10 + uint64_t(*p - '0'); + ++p; + } + exponent = answer.fraction.ptr - p + exp_number; + } + // We have now corrected both exponent and i, to a truncated value + } + } + answer.exponent = exponent; + answer.mantissa = i; + return answer; +} + +} // namespace fast_float + +#endif + +#ifndef FASTFLOAT_FAST_TABLE_H +#define FASTFLOAT_FAST_TABLE_H + +namespace fast_float { + +/** + * When mapping numbers from decimal to binary, + * we go from w * 10^q to m * 2^p but we have + * 10^q = 5^q * 2^q, so effectively + * we are trying to match + * w * 2^q * 5^q to m * 2^p. Thus the powers of two + * are not a concern since they can be represented + * exactly using the binary notation, only the powers of five + * affect the binary significand. + */ + +/** + * The smallest non-zero float (binary64) is 2^−1074. + * We take as input numbers of the form w x 10^q where w < 2^64. + * We have that w * 10^-343 < 2^(64-344) 5^-343 < 2^-1076. + * However, we have that + * (2^64-1) * 10^-342 = (2^64-1) * 2^-342 * 5^-342 > 2^−1074. + * Thus it is possible for a number of the form w * 10^-342 where + * w is a 64-bit value to be a non-zero floating-point number. + ********* + * Any number of form w * 10^309 where w>= 1 is going to be + * infinite in binary64 so we never need to worry about powers + * of 5 greater than 308. + */ +template +struct powers_template { + +constexpr static int smallest_power_of_five = binary_format::smallest_power_of_ten(); +constexpr static int largest_power_of_five = binary_format::largest_power_of_ten(); +constexpr static int number_of_entries = 2 * (largest_power_of_five - smallest_power_of_five + 1); +// Powers of five from 5^-342 all the way to 5^308 rounded toward one. +static const uint64_t power_of_five_128[number_of_entries]; +}; + +template +const uint64_t powers_template::power_of_five_128[number_of_entries] = { + 0xeef453d6923bd65a,0x113faa2906a13b3f, + 0x9558b4661b6565f8,0x4ac7ca59a424c507, + 0xbaaee17fa23ebf76,0x5d79bcf00d2df649, + 0xe95a99df8ace6f53,0xf4d82c2c107973dc, + 0x91d8a02bb6c10594,0x79071b9b8a4be869, + 0xb64ec836a47146f9,0x9748e2826cdee284, + 0xe3e27a444d8d98b7,0xfd1b1b2308169b25, + 0x8e6d8c6ab0787f72,0xfe30f0f5e50e20f7, + 0xb208ef855c969f4f,0xbdbd2d335e51a935, + 0xde8b2b66b3bc4723,0xad2c788035e61382, + 0x8b16fb203055ac76,0x4c3bcb5021afcc31, + 0xaddcb9e83c6b1793,0xdf4abe242a1bbf3d, + 0xd953e8624b85dd78,0xd71d6dad34a2af0d, + 0x87d4713d6f33aa6b,0x8672648c40e5ad68, + 0xa9c98d8ccb009506,0x680efdaf511f18c2, + 0xd43bf0effdc0ba48,0x212bd1b2566def2, + 0x84a57695fe98746d,0x14bb630f7604b57, + 0xa5ced43b7e3e9188,0x419ea3bd35385e2d, + 0xcf42894a5dce35ea,0x52064cac828675b9, + 0x818995ce7aa0e1b2,0x7343efebd1940993, + 0xa1ebfb4219491a1f,0x1014ebe6c5f90bf8, + 0xca66fa129f9b60a6,0xd41a26e077774ef6, + 0xfd00b897478238d0,0x8920b098955522b4, + 0x9e20735e8cb16382,0x55b46e5f5d5535b0, + 0xc5a890362fddbc62,0xeb2189f734aa831d, + 0xf712b443bbd52b7b,0xa5e9ec7501d523e4, + 0x9a6bb0aa55653b2d,0x47b233c92125366e, + 0xc1069cd4eabe89f8,0x999ec0bb696e840a, + 0xf148440a256e2c76,0xc00670ea43ca250d, + 0x96cd2a865764dbca,0x380406926a5e5728, + 0xbc807527ed3e12bc,0xc605083704f5ecf2, + 0xeba09271e88d976b,0xf7864a44c633682e, + 0x93445b8731587ea3,0x7ab3ee6afbe0211d, + 0xb8157268fdae9e4c,0x5960ea05bad82964, + 0xe61acf033d1a45df,0x6fb92487298e33bd, + 0x8fd0c16206306bab,0xa5d3b6d479f8e056, + 0xb3c4f1ba87bc8696,0x8f48a4899877186c, + 0xe0b62e2929aba83c,0x331acdabfe94de87, + 0x8c71dcd9ba0b4925,0x9ff0c08b7f1d0b14, + 0xaf8e5410288e1b6f,0x7ecf0ae5ee44dd9, + 0xdb71e91432b1a24a,0xc9e82cd9f69d6150, + 0x892731ac9faf056e,0xbe311c083a225cd2, + 0xab70fe17c79ac6ca,0x6dbd630a48aaf406, + 0xd64d3d9db981787d,0x92cbbccdad5b108, + 0x85f0468293f0eb4e,0x25bbf56008c58ea5, + 0xa76c582338ed2621,0xaf2af2b80af6f24e, + 0xd1476e2c07286faa,0x1af5af660db4aee1, + 0x82cca4db847945ca,0x50d98d9fc890ed4d, + 0xa37fce126597973c,0xe50ff107bab528a0, + 0xcc5fc196fefd7d0c,0x1e53ed49a96272c8, + 0xff77b1fcbebcdc4f,0x25e8e89c13bb0f7a, + 0x9faacf3df73609b1,0x77b191618c54e9ac, + 0xc795830d75038c1d,0xd59df5b9ef6a2417, + 0xf97ae3d0d2446f25,0x4b0573286b44ad1d, + 0x9becce62836ac577,0x4ee367f9430aec32, + 0xc2e801fb244576d5,0x229c41f793cda73f, + 0xf3a20279ed56d48a,0x6b43527578c1110f, + 0x9845418c345644d6,0x830a13896b78aaa9, + 0xbe5691ef416bd60c,0x23cc986bc656d553, + 0xedec366b11c6cb8f,0x2cbfbe86b7ec8aa8, + 0x94b3a202eb1c3f39,0x7bf7d71432f3d6a9, + 0xb9e08a83a5e34f07,0xdaf5ccd93fb0cc53, + 0xe858ad248f5c22c9,0xd1b3400f8f9cff68, + 0x91376c36d99995be,0x23100809b9c21fa1, + 0xb58547448ffffb2d,0xabd40a0c2832a78a, + 0xe2e69915b3fff9f9,0x16c90c8f323f516c, + 0x8dd01fad907ffc3b,0xae3da7d97f6792e3, + 0xb1442798f49ffb4a,0x99cd11cfdf41779c, + 0xdd95317f31c7fa1d,0x40405643d711d583, + 0x8a7d3eef7f1cfc52,0x482835ea666b2572, + 0xad1c8eab5ee43b66,0xda3243650005eecf, + 0xd863b256369d4a40,0x90bed43e40076a82, + 0x873e4f75e2224e68,0x5a7744a6e804a291, + 0xa90de3535aaae202,0x711515d0a205cb36, + 0xd3515c2831559a83,0xd5a5b44ca873e03, + 0x8412d9991ed58091,0xe858790afe9486c2, + 0xa5178fff668ae0b6,0x626e974dbe39a872, + 0xce5d73ff402d98e3,0xfb0a3d212dc8128f, + 0x80fa687f881c7f8e,0x7ce66634bc9d0b99, + 0xa139029f6a239f72,0x1c1fffc1ebc44e80, + 0xc987434744ac874e,0xa327ffb266b56220, + 0xfbe9141915d7a922,0x4bf1ff9f0062baa8, + 0x9d71ac8fada6c9b5,0x6f773fc3603db4a9, + 0xc4ce17b399107c22,0xcb550fb4384d21d3, + 0xf6019da07f549b2b,0x7e2a53a146606a48, + 0x99c102844f94e0fb,0x2eda7444cbfc426d, + 0xc0314325637a1939,0xfa911155fefb5308, + 0xf03d93eebc589f88,0x793555ab7eba27ca, + 0x96267c7535b763b5,0x4bc1558b2f3458de, + 0xbbb01b9283253ca2,0x9eb1aaedfb016f16, + 0xea9c227723ee8bcb,0x465e15a979c1cadc, + 0x92a1958a7675175f,0xbfacd89ec191ec9, + 0xb749faed14125d36,0xcef980ec671f667b, + 0xe51c79a85916f484,0x82b7e12780e7401a, + 0x8f31cc0937ae58d2,0xd1b2ecb8b0908810, + 0xb2fe3f0b8599ef07,0x861fa7e6dcb4aa15, + 0xdfbdcece67006ac9,0x67a791e093e1d49a, + 0x8bd6a141006042bd,0xe0c8bb2c5c6d24e0, + 0xaecc49914078536d,0x58fae9f773886e18, + 0xda7f5bf590966848,0xaf39a475506a899e, + 0x888f99797a5e012d,0x6d8406c952429603, + 0xaab37fd7d8f58178,0xc8e5087ba6d33b83, + 0xd5605fcdcf32e1d6,0xfb1e4a9a90880a64, + 0x855c3be0a17fcd26,0x5cf2eea09a55067f, + 0xa6b34ad8c9dfc06f,0xf42faa48c0ea481e, + 0xd0601d8efc57b08b,0xf13b94daf124da26, + 0x823c12795db6ce57,0x76c53d08d6b70858, + 0xa2cb1717b52481ed,0x54768c4b0c64ca6e, + 0xcb7ddcdda26da268,0xa9942f5dcf7dfd09, + 0xfe5d54150b090b02,0xd3f93b35435d7c4c, + 0x9efa548d26e5a6e1,0xc47bc5014a1a6daf, + 0xc6b8e9b0709f109a,0x359ab6419ca1091b, + 0xf867241c8cc6d4c0,0xc30163d203c94b62, + 0x9b407691d7fc44f8,0x79e0de63425dcf1d, + 0xc21094364dfb5636,0x985915fc12f542e4, + 0xf294b943e17a2bc4,0x3e6f5b7b17b2939d, + 0x979cf3ca6cec5b5a,0xa705992ceecf9c42, + 0xbd8430bd08277231,0x50c6ff782a838353, + 0xece53cec4a314ebd,0xa4f8bf5635246428, + 0x940f4613ae5ed136,0x871b7795e136be99, + 0xb913179899f68584,0x28e2557b59846e3f, + 0xe757dd7ec07426e5,0x331aeada2fe589cf, + 0x9096ea6f3848984f,0x3ff0d2c85def7621, + 0xb4bca50b065abe63,0xfed077a756b53a9, + 0xe1ebce4dc7f16dfb,0xd3e8495912c62894, + 0x8d3360f09cf6e4bd,0x64712dd7abbbd95c, + 0xb080392cc4349dec,0xbd8d794d96aacfb3, + 0xdca04777f541c567,0xecf0d7a0fc5583a0, + 0x89e42caaf9491b60,0xf41686c49db57244, + 0xac5d37d5b79b6239,0x311c2875c522ced5, + 0xd77485cb25823ac7,0x7d633293366b828b, + 0x86a8d39ef77164bc,0xae5dff9c02033197, + 0xa8530886b54dbdeb,0xd9f57f830283fdfc, + 0xd267caa862a12d66,0xd072df63c324fd7b, + 0x8380dea93da4bc60,0x4247cb9e59f71e6d, + 0xa46116538d0deb78,0x52d9be85f074e608, + 0xcd795be870516656,0x67902e276c921f8b, + 0x806bd9714632dff6,0xba1cd8a3db53b6, + 0xa086cfcd97bf97f3,0x80e8a40eccd228a4, + 0xc8a883c0fdaf7df0,0x6122cd128006b2cd, + 0xfad2a4b13d1b5d6c,0x796b805720085f81, + 0x9cc3a6eec6311a63,0xcbe3303674053bb0, + 0xc3f490aa77bd60fc,0xbedbfc4411068a9c, + 0xf4f1b4d515acb93b,0xee92fb5515482d44, + 0x991711052d8bf3c5,0x751bdd152d4d1c4a, + 0xbf5cd54678eef0b6,0xd262d45a78a0635d, + 0xef340a98172aace4,0x86fb897116c87c34, + 0x9580869f0e7aac0e,0xd45d35e6ae3d4da0, + 0xbae0a846d2195712,0x8974836059cca109, + 0xe998d258869facd7,0x2bd1a438703fc94b, + 0x91ff83775423cc06,0x7b6306a34627ddcf, + 0xb67f6455292cbf08,0x1a3bc84c17b1d542, + 0xe41f3d6a7377eeca,0x20caba5f1d9e4a93, + 0x8e938662882af53e,0x547eb47b7282ee9c, + 0xb23867fb2a35b28d,0xe99e619a4f23aa43, + 0xdec681f9f4c31f31,0x6405fa00e2ec94d4, + 0x8b3c113c38f9f37e,0xde83bc408dd3dd04, + 0xae0b158b4738705e,0x9624ab50b148d445, + 0xd98ddaee19068c76,0x3badd624dd9b0957, + 0x87f8a8d4cfa417c9,0xe54ca5d70a80e5d6, + 0xa9f6d30a038d1dbc,0x5e9fcf4ccd211f4c, + 0xd47487cc8470652b,0x7647c3200069671f, + 0x84c8d4dfd2c63f3b,0x29ecd9f40041e073, + 0xa5fb0a17c777cf09,0xf468107100525890, + 0xcf79cc9db955c2cc,0x7182148d4066eeb4, + 0x81ac1fe293d599bf,0xc6f14cd848405530, + 0xa21727db38cb002f,0xb8ada00e5a506a7c, + 0xca9cf1d206fdc03b,0xa6d90811f0e4851c, + 0xfd442e4688bd304a,0x908f4a166d1da663, + 0x9e4a9cec15763e2e,0x9a598e4e043287fe, + 0xc5dd44271ad3cdba,0x40eff1e1853f29fd, + 0xf7549530e188c128,0xd12bee59e68ef47c, + 0x9a94dd3e8cf578b9,0x82bb74f8301958ce, + 0xc13a148e3032d6e7,0xe36a52363c1faf01, + 0xf18899b1bc3f8ca1,0xdc44e6c3cb279ac1, + 0x96f5600f15a7b7e5,0x29ab103a5ef8c0b9, + 0xbcb2b812db11a5de,0x7415d448f6b6f0e7, + 0xebdf661791d60f56,0x111b495b3464ad21, + 0x936b9fcebb25c995,0xcab10dd900beec34, + 0xb84687c269ef3bfb,0x3d5d514f40eea742, + 0xe65829b3046b0afa,0xcb4a5a3112a5112, + 0x8ff71a0fe2c2e6dc,0x47f0e785eaba72ab, + 0xb3f4e093db73a093,0x59ed216765690f56, + 0xe0f218b8d25088b8,0x306869c13ec3532c, + 0x8c974f7383725573,0x1e414218c73a13fb, + 0xafbd2350644eeacf,0xe5d1929ef90898fa, + 0xdbac6c247d62a583,0xdf45f746b74abf39, + 0x894bc396ce5da772,0x6b8bba8c328eb783, + 0xab9eb47c81f5114f,0x66ea92f3f326564, + 0xd686619ba27255a2,0xc80a537b0efefebd, + 0x8613fd0145877585,0xbd06742ce95f5f36, + 0xa798fc4196e952e7,0x2c48113823b73704, + 0xd17f3b51fca3a7a0,0xf75a15862ca504c5, + 0x82ef85133de648c4,0x9a984d73dbe722fb, + 0xa3ab66580d5fdaf5,0xc13e60d0d2e0ebba, + 0xcc963fee10b7d1b3,0x318df905079926a8, + 0xffbbcfe994e5c61f,0xfdf17746497f7052, + 0x9fd561f1fd0f9bd3,0xfeb6ea8bedefa633, + 0xc7caba6e7c5382c8,0xfe64a52ee96b8fc0, + 0xf9bd690a1b68637b,0x3dfdce7aa3c673b0, + 0x9c1661a651213e2d,0x6bea10ca65c084e, + 0xc31bfa0fe5698db8,0x486e494fcff30a62, + 0xf3e2f893dec3f126,0x5a89dba3c3efccfa, + 0x986ddb5c6b3a76b7,0xf89629465a75e01c, + 0xbe89523386091465,0xf6bbb397f1135823, + 0xee2ba6c0678b597f,0x746aa07ded582e2c, + 0x94db483840b717ef,0xa8c2a44eb4571cdc, + 0xba121a4650e4ddeb,0x92f34d62616ce413, + 0xe896a0d7e51e1566,0x77b020baf9c81d17, + 0x915e2486ef32cd60,0xace1474dc1d122e, + 0xb5b5ada8aaff80b8,0xd819992132456ba, + 0xe3231912d5bf60e6,0x10e1fff697ed6c69, + 0x8df5efabc5979c8f,0xca8d3ffa1ef463c1, + 0xb1736b96b6fd83b3,0xbd308ff8a6b17cb2, + 0xddd0467c64bce4a0,0xac7cb3f6d05ddbde, + 0x8aa22c0dbef60ee4,0x6bcdf07a423aa96b, + 0xad4ab7112eb3929d,0x86c16c98d2c953c6, + 0xd89d64d57a607744,0xe871c7bf077ba8b7, + 0x87625f056c7c4a8b,0x11471cd764ad4972, + 0xa93af6c6c79b5d2d,0xd598e40d3dd89bcf, + 0xd389b47879823479,0x4aff1d108d4ec2c3, + 0x843610cb4bf160cb,0xcedf722a585139ba, + 0xa54394fe1eedb8fe,0xc2974eb4ee658828, + 0xce947a3da6a9273e,0x733d226229feea32, + 0x811ccc668829b887,0x806357d5a3f525f, + 0xa163ff802a3426a8,0xca07c2dcb0cf26f7, + 0xc9bcff6034c13052,0xfc89b393dd02f0b5, + 0xfc2c3f3841f17c67,0xbbac2078d443ace2, + 0x9d9ba7832936edc0,0xd54b944b84aa4c0d, + 0xc5029163f384a931,0xa9e795e65d4df11, + 0xf64335bcf065d37d,0x4d4617b5ff4a16d5, + 0x99ea0196163fa42e,0x504bced1bf8e4e45, + 0xc06481fb9bcf8d39,0xe45ec2862f71e1d6, + 0xf07da27a82c37088,0x5d767327bb4e5a4c, + 0x964e858c91ba2655,0x3a6a07f8d510f86f, + 0xbbe226efb628afea,0x890489f70a55368b, + 0xeadab0aba3b2dbe5,0x2b45ac74ccea842e, + 0x92c8ae6b464fc96f,0x3b0b8bc90012929d, + 0xb77ada0617e3bbcb,0x9ce6ebb40173744, + 0xe55990879ddcaabd,0xcc420a6a101d0515, + 0x8f57fa54c2a9eab6,0x9fa946824a12232d, + 0xb32df8e9f3546564,0x47939822dc96abf9, + 0xdff9772470297ebd,0x59787e2b93bc56f7, + 0x8bfbea76c619ef36,0x57eb4edb3c55b65a, + 0xaefae51477a06b03,0xede622920b6b23f1, + 0xdab99e59958885c4,0xe95fab368e45eced, + 0x88b402f7fd75539b,0x11dbcb0218ebb414, + 0xaae103b5fcd2a881,0xd652bdc29f26a119, + 0xd59944a37c0752a2,0x4be76d3346f0495f, + 0x857fcae62d8493a5,0x6f70a4400c562ddb, + 0xa6dfbd9fb8e5b88e,0xcb4ccd500f6bb952, + 0xd097ad07a71f26b2,0x7e2000a41346a7a7, + 0x825ecc24c873782f,0x8ed400668c0c28c8, + 0xa2f67f2dfa90563b,0x728900802f0f32fa, + 0xcbb41ef979346bca,0x4f2b40a03ad2ffb9, + 0xfea126b7d78186bc,0xe2f610c84987bfa8, + 0x9f24b832e6b0f436,0xdd9ca7d2df4d7c9, + 0xc6ede63fa05d3143,0x91503d1c79720dbb, + 0xf8a95fcf88747d94,0x75a44c6397ce912a, + 0x9b69dbe1b548ce7c,0xc986afbe3ee11aba, + 0xc24452da229b021b,0xfbe85badce996168, + 0xf2d56790ab41c2a2,0xfae27299423fb9c3, + 0x97c560ba6b0919a5,0xdccd879fc967d41a, + 0xbdb6b8e905cb600f,0x5400e987bbc1c920, + 0xed246723473e3813,0x290123e9aab23b68, + 0x9436c0760c86e30b,0xf9a0b6720aaf6521, + 0xb94470938fa89bce,0xf808e40e8d5b3e69, + 0xe7958cb87392c2c2,0xb60b1d1230b20e04, + 0x90bd77f3483bb9b9,0xb1c6f22b5e6f48c2, + 0xb4ecd5f01a4aa828,0x1e38aeb6360b1af3, + 0xe2280b6c20dd5232,0x25c6da63c38de1b0, + 0x8d590723948a535f,0x579c487e5a38ad0e, + 0xb0af48ec79ace837,0x2d835a9df0c6d851, + 0xdcdb1b2798182244,0xf8e431456cf88e65, + 0x8a08f0f8bf0f156b,0x1b8e9ecb641b58ff, + 0xac8b2d36eed2dac5,0xe272467e3d222f3f, + 0xd7adf884aa879177,0x5b0ed81dcc6abb0f, + 0x86ccbb52ea94baea,0x98e947129fc2b4e9, + 0xa87fea27a539e9a5,0x3f2398d747b36224, + 0xd29fe4b18e88640e,0x8eec7f0d19a03aad, + 0x83a3eeeef9153e89,0x1953cf68300424ac, + 0xa48ceaaab75a8e2b,0x5fa8c3423c052dd7, + 0xcdb02555653131b6,0x3792f412cb06794d, + 0x808e17555f3ebf11,0xe2bbd88bbee40bd0, + 0xa0b19d2ab70e6ed6,0x5b6aceaeae9d0ec4, + 0xc8de047564d20a8b,0xf245825a5a445275, + 0xfb158592be068d2e,0xeed6e2f0f0d56712, + 0x9ced737bb6c4183d,0x55464dd69685606b, + 0xc428d05aa4751e4c,0xaa97e14c3c26b886, + 0xf53304714d9265df,0xd53dd99f4b3066a8, + 0x993fe2c6d07b7fab,0xe546a8038efe4029, + 0xbf8fdb78849a5f96,0xde98520472bdd033, + 0xef73d256a5c0f77c,0x963e66858f6d4440, + 0x95a8637627989aad,0xdde7001379a44aa8, + 0xbb127c53b17ec159,0x5560c018580d5d52, + 0xe9d71b689dde71af,0xaab8f01e6e10b4a6, + 0x9226712162ab070d,0xcab3961304ca70e8, + 0xb6b00d69bb55c8d1,0x3d607b97c5fd0d22, + 0xe45c10c42a2b3b05,0x8cb89a7db77c506a, + 0x8eb98a7a9a5b04e3,0x77f3608e92adb242, + 0xb267ed1940f1c61c,0x55f038b237591ed3, + 0xdf01e85f912e37a3,0x6b6c46dec52f6688, + 0x8b61313bbabce2c6,0x2323ac4b3b3da015, + 0xae397d8aa96c1b77,0xabec975e0a0d081a, + 0xd9c7dced53c72255,0x96e7bd358c904a21, + 0x881cea14545c7575,0x7e50d64177da2e54, + 0xaa242499697392d2,0xdde50bd1d5d0b9e9, + 0xd4ad2dbfc3d07787,0x955e4ec64b44e864, + 0x84ec3c97da624ab4,0xbd5af13bef0b113e, + 0xa6274bbdd0fadd61,0xecb1ad8aeacdd58e, + 0xcfb11ead453994ba,0x67de18eda5814af2, + 0x81ceb32c4b43fcf4,0x80eacf948770ced7, + 0xa2425ff75e14fc31,0xa1258379a94d028d, + 0xcad2f7f5359a3b3e,0x96ee45813a04330, + 0xfd87b5f28300ca0d,0x8bca9d6e188853fc, + 0x9e74d1b791e07e48,0x775ea264cf55347e, + 0xc612062576589dda,0x95364afe032a819e, + 0xf79687aed3eec551,0x3a83ddbd83f52205, + 0x9abe14cd44753b52,0xc4926a9672793543, + 0xc16d9a0095928a27,0x75b7053c0f178294, + 0xf1c90080baf72cb1,0x5324c68b12dd6339, + 0x971da05074da7bee,0xd3f6fc16ebca5e04, + 0xbce5086492111aea,0x88f4bb1ca6bcf585, + 0xec1e4a7db69561a5,0x2b31e9e3d06c32e6, + 0x9392ee8e921d5d07,0x3aff322e62439fd0, + 0xb877aa3236a4b449,0x9befeb9fad487c3, + 0xe69594bec44de15b,0x4c2ebe687989a9b4, + 0x901d7cf73ab0acd9,0xf9d37014bf60a11, + 0xb424dc35095cd80f,0x538484c19ef38c95, + 0xe12e13424bb40e13,0x2865a5f206b06fba, + 0x8cbccc096f5088cb,0xf93f87b7442e45d4, + 0xafebff0bcb24aafe,0xf78f69a51539d749, + 0xdbe6fecebdedd5be,0xb573440e5a884d1c, + 0x89705f4136b4a597,0x31680a88f8953031, + 0xabcc77118461cefc,0xfdc20d2b36ba7c3e, + 0xd6bf94d5e57a42bc,0x3d32907604691b4d, + 0x8637bd05af6c69b5,0xa63f9a49c2c1b110, + 0xa7c5ac471b478423,0xfcf80dc33721d54, + 0xd1b71758e219652b,0xd3c36113404ea4a9, + 0x83126e978d4fdf3b,0x645a1cac083126ea, + 0xa3d70a3d70a3d70a,0x3d70a3d70a3d70a4, + 0xcccccccccccccccc,0xcccccccccccccccd, + 0x8000000000000000,0x0, + 0xa000000000000000,0x0, + 0xc800000000000000,0x0, + 0xfa00000000000000,0x0, + 0x9c40000000000000,0x0, + 0xc350000000000000,0x0, + 0xf424000000000000,0x0, + 0x9896800000000000,0x0, + 0xbebc200000000000,0x0, + 0xee6b280000000000,0x0, + 0x9502f90000000000,0x0, + 0xba43b74000000000,0x0, + 0xe8d4a51000000000,0x0, + 0x9184e72a00000000,0x0, + 0xb5e620f480000000,0x0, + 0xe35fa931a0000000,0x0, + 0x8e1bc9bf04000000,0x0, + 0xb1a2bc2ec5000000,0x0, + 0xde0b6b3a76400000,0x0, + 0x8ac7230489e80000,0x0, + 0xad78ebc5ac620000,0x0, + 0xd8d726b7177a8000,0x0, + 0x878678326eac9000,0x0, + 0xa968163f0a57b400,0x0, + 0xd3c21bcecceda100,0x0, + 0x84595161401484a0,0x0, + 0xa56fa5b99019a5c8,0x0, + 0xcecb8f27f4200f3a,0x0, + 0x813f3978f8940984,0x4000000000000000, + 0xa18f07d736b90be5,0x5000000000000000, + 0xc9f2c9cd04674ede,0xa400000000000000, + 0xfc6f7c4045812296,0x4d00000000000000, + 0x9dc5ada82b70b59d,0xf020000000000000, + 0xc5371912364ce305,0x6c28000000000000, + 0xf684df56c3e01bc6,0xc732000000000000, + 0x9a130b963a6c115c,0x3c7f400000000000, + 0xc097ce7bc90715b3,0x4b9f100000000000, + 0xf0bdc21abb48db20,0x1e86d40000000000, + 0x96769950b50d88f4,0x1314448000000000, + 0xbc143fa4e250eb31,0x17d955a000000000, + 0xeb194f8e1ae525fd,0x5dcfab0800000000, + 0x92efd1b8d0cf37be,0x5aa1cae500000000, + 0xb7abc627050305ad,0xf14a3d9e40000000, + 0xe596b7b0c643c719,0x6d9ccd05d0000000, + 0x8f7e32ce7bea5c6f,0xe4820023a2000000, + 0xb35dbf821ae4f38b,0xdda2802c8a800000, + 0xe0352f62a19e306e,0xd50b2037ad200000, + 0x8c213d9da502de45,0x4526f422cc340000, + 0xaf298d050e4395d6,0x9670b12b7f410000, + 0xdaf3f04651d47b4c,0x3c0cdd765f114000, + 0x88d8762bf324cd0f,0xa5880a69fb6ac800, + 0xab0e93b6efee0053,0x8eea0d047a457a00, + 0xd5d238a4abe98068,0x72a4904598d6d880, + 0x85a36366eb71f041,0x47a6da2b7f864750, + 0xa70c3c40a64e6c51,0x999090b65f67d924, + 0xd0cf4b50cfe20765,0xfff4b4e3f741cf6d, + 0x82818f1281ed449f,0xbff8f10e7a8921a4, + 0xa321f2d7226895c7,0xaff72d52192b6a0d, + 0xcbea6f8ceb02bb39,0x9bf4f8a69f764490, + 0xfee50b7025c36a08,0x2f236d04753d5b4, + 0x9f4f2726179a2245,0x1d762422c946590, + 0xc722f0ef9d80aad6,0x424d3ad2b7b97ef5, + 0xf8ebad2b84e0d58b,0xd2e0898765a7deb2, + 0x9b934c3b330c8577,0x63cc55f49f88eb2f, + 0xc2781f49ffcfa6d5,0x3cbf6b71c76b25fb, + 0xf316271c7fc3908a,0x8bef464e3945ef7a, + 0x97edd871cfda3a56,0x97758bf0e3cbb5ac, + 0xbde94e8e43d0c8ec,0x3d52eeed1cbea317, + 0xed63a231d4c4fb27,0x4ca7aaa863ee4bdd, + 0x945e455f24fb1cf8,0x8fe8caa93e74ef6a, + 0xb975d6b6ee39e436,0xb3e2fd538e122b44, + 0xe7d34c64a9c85d44,0x60dbbca87196b616, + 0x90e40fbeea1d3a4a,0xbc8955e946fe31cd, + 0xb51d13aea4a488dd,0x6babab6398bdbe41, + 0xe264589a4dcdab14,0xc696963c7eed2dd1, + 0x8d7eb76070a08aec,0xfc1e1de5cf543ca2, + 0xb0de65388cc8ada8,0x3b25a55f43294bcb, + 0xdd15fe86affad912,0x49ef0eb713f39ebe, + 0x8a2dbf142dfcc7ab,0x6e3569326c784337, + 0xacb92ed9397bf996,0x49c2c37f07965404, + 0xd7e77a8f87daf7fb,0xdc33745ec97be906, + 0x86f0ac99b4e8dafd,0x69a028bb3ded71a3, + 0xa8acd7c0222311bc,0xc40832ea0d68ce0c, + 0xd2d80db02aabd62b,0xf50a3fa490c30190, + 0x83c7088e1aab65db,0x792667c6da79e0fa, + 0xa4b8cab1a1563f52,0x577001b891185938, + 0xcde6fd5e09abcf26,0xed4c0226b55e6f86, + 0x80b05e5ac60b6178,0x544f8158315b05b4, + 0xa0dc75f1778e39d6,0x696361ae3db1c721, + 0xc913936dd571c84c,0x3bc3a19cd1e38e9, + 0xfb5878494ace3a5f,0x4ab48a04065c723, + 0x9d174b2dcec0e47b,0x62eb0d64283f9c76, + 0xc45d1df942711d9a,0x3ba5d0bd324f8394, + 0xf5746577930d6500,0xca8f44ec7ee36479, + 0x9968bf6abbe85f20,0x7e998b13cf4e1ecb, + 0xbfc2ef456ae276e8,0x9e3fedd8c321a67e, + 0xefb3ab16c59b14a2,0xc5cfe94ef3ea101e, + 0x95d04aee3b80ece5,0xbba1f1d158724a12, + 0xbb445da9ca61281f,0x2a8a6e45ae8edc97, + 0xea1575143cf97226,0xf52d09d71a3293bd, + 0x924d692ca61be758,0x593c2626705f9c56, + 0xb6e0c377cfa2e12e,0x6f8b2fb00c77836c, + 0xe498f455c38b997a,0xb6dfb9c0f956447, + 0x8edf98b59a373fec,0x4724bd4189bd5eac, + 0xb2977ee300c50fe7,0x58edec91ec2cb657, + 0xdf3d5e9bc0f653e1,0x2f2967b66737e3ed, + 0x8b865b215899f46c,0xbd79e0d20082ee74, + 0xae67f1e9aec07187,0xecd8590680a3aa11, + 0xda01ee641a708de9,0xe80e6f4820cc9495, + 0x884134fe908658b2,0x3109058d147fdcdd, + 0xaa51823e34a7eede,0xbd4b46f0599fd415, + 0xd4e5e2cdc1d1ea96,0x6c9e18ac7007c91a, + 0x850fadc09923329e,0x3e2cf6bc604ddb0, + 0xa6539930bf6bff45,0x84db8346b786151c, + 0xcfe87f7cef46ff16,0xe612641865679a63, + 0x81f14fae158c5f6e,0x4fcb7e8f3f60c07e, + 0xa26da3999aef7749,0xe3be5e330f38f09d, + 0xcb090c8001ab551c,0x5cadf5bfd3072cc5, + 0xfdcb4fa002162a63,0x73d9732fc7c8f7f6, + 0x9e9f11c4014dda7e,0x2867e7fddcdd9afa, + 0xc646d63501a1511d,0xb281e1fd541501b8, + 0xf7d88bc24209a565,0x1f225a7ca91a4226, + 0x9ae757596946075f,0x3375788de9b06958, + 0xc1a12d2fc3978937,0x52d6b1641c83ae, + 0xf209787bb47d6b84,0xc0678c5dbd23a49a, + 0x9745eb4d50ce6332,0xf840b7ba963646e0, + 0xbd176620a501fbff,0xb650e5a93bc3d898, + 0xec5d3fa8ce427aff,0xa3e51f138ab4cebe, + 0x93ba47c980e98cdf,0xc66f336c36b10137, + 0xb8a8d9bbe123f017,0xb80b0047445d4184, + 0xe6d3102ad96cec1d,0xa60dc059157491e5, + 0x9043ea1ac7e41392,0x87c89837ad68db2f, + 0xb454e4a179dd1877,0x29babe4598c311fb, + 0xe16a1dc9d8545e94,0xf4296dd6fef3d67a, + 0x8ce2529e2734bb1d,0x1899e4a65f58660c, + 0xb01ae745b101e9e4,0x5ec05dcff72e7f8f, + 0xdc21a1171d42645d,0x76707543f4fa1f73, + 0x899504ae72497eba,0x6a06494a791c53a8, + 0xabfa45da0edbde69,0x487db9d17636892, + 0xd6f8d7509292d603,0x45a9d2845d3c42b6, + 0x865b86925b9bc5c2,0xb8a2392ba45a9b2, + 0xa7f26836f282b732,0x8e6cac7768d7141e, + 0xd1ef0244af2364ff,0x3207d795430cd926, + 0x8335616aed761f1f,0x7f44e6bd49e807b8, + 0xa402b9c5a8d3a6e7,0x5f16206c9c6209a6, + 0xcd036837130890a1,0x36dba887c37a8c0f, + 0x802221226be55a64,0xc2494954da2c9789, + 0xa02aa96b06deb0fd,0xf2db9baa10b7bd6c, + 0xc83553c5c8965d3d,0x6f92829494e5acc7, + 0xfa42a8b73abbf48c,0xcb772339ba1f17f9, + 0x9c69a97284b578d7,0xff2a760414536efb, + 0xc38413cf25e2d70d,0xfef5138519684aba, + 0xf46518c2ef5b8cd1,0x7eb258665fc25d69, + 0x98bf2f79d5993802,0xef2f773ffbd97a61, + 0xbeeefb584aff8603,0xaafb550ffacfd8fa, + 0xeeaaba2e5dbf6784,0x95ba2a53f983cf38, + 0x952ab45cfa97a0b2,0xdd945a747bf26183, + 0xba756174393d88df,0x94f971119aeef9e4, + 0xe912b9d1478ceb17,0x7a37cd5601aab85d, + 0x91abb422ccb812ee,0xac62e055c10ab33a, + 0xb616a12b7fe617aa,0x577b986b314d6009, + 0xe39c49765fdf9d94,0xed5a7e85fda0b80b, + 0x8e41ade9fbebc27d,0x14588f13be847307, + 0xb1d219647ae6b31c,0x596eb2d8ae258fc8, + 0xde469fbd99a05fe3,0x6fca5f8ed9aef3bb, + 0x8aec23d680043bee,0x25de7bb9480d5854, + 0xada72ccc20054ae9,0xaf561aa79a10ae6a, + 0xd910f7ff28069da4,0x1b2ba1518094da04, + 0x87aa9aff79042286,0x90fb44d2f05d0842, + 0xa99541bf57452b28,0x353a1607ac744a53, + 0xd3fa922f2d1675f2,0x42889b8997915ce8, + 0x847c9b5d7c2e09b7,0x69956135febada11, + 0xa59bc234db398c25,0x43fab9837e699095, + 0xcf02b2c21207ef2e,0x94f967e45e03f4bb, + 0x8161afb94b44f57d,0x1d1be0eebac278f5, + 0xa1ba1ba79e1632dc,0x6462d92a69731732, + 0xca28a291859bbf93,0x7d7b8f7503cfdcfe, + 0xfcb2cb35e702af78,0x5cda735244c3d43e, + 0x9defbf01b061adab,0x3a0888136afa64a7, + 0xc56baec21c7a1916,0x88aaa1845b8fdd0, + 0xf6c69a72a3989f5b,0x8aad549e57273d45, + 0x9a3c2087a63f6399,0x36ac54e2f678864b, + 0xc0cb28a98fcf3c7f,0x84576a1bb416a7dd, + 0xf0fdf2d3f3c30b9f,0x656d44a2a11c51d5, + 0x969eb7c47859e743,0x9f644ae5a4b1b325, + 0xbc4665b596706114,0x873d5d9f0dde1fee, + 0xeb57ff22fc0c7959,0xa90cb506d155a7ea, + 0x9316ff75dd87cbd8,0x9a7f12442d588f2, + 0xb7dcbf5354e9bece,0xc11ed6d538aeb2f, + 0xe5d3ef282a242e81,0x8f1668c8a86da5fa, + 0x8fa475791a569d10,0xf96e017d694487bc, + 0xb38d92d760ec4455,0x37c981dcc395a9ac, + 0xe070f78d3927556a,0x85bbe253f47b1417, + 0x8c469ab843b89562,0x93956d7478ccec8e, + 0xaf58416654a6babb,0x387ac8d1970027b2, + 0xdb2e51bfe9d0696a,0x6997b05fcc0319e, + 0x88fcf317f22241e2,0x441fece3bdf81f03, + 0xab3c2fddeeaad25a,0xd527e81cad7626c3, + 0xd60b3bd56a5586f1,0x8a71e223d8d3b074, + 0x85c7056562757456,0xf6872d5667844e49, + 0xa738c6bebb12d16c,0xb428f8ac016561db, + 0xd106f86e69d785c7,0xe13336d701beba52, + 0x82a45b450226b39c,0xecc0024661173473, + 0xa34d721642b06084,0x27f002d7f95d0190, + 0xcc20ce9bd35c78a5,0x31ec038df7b441f4, + 0xff290242c83396ce,0x7e67047175a15271, + 0x9f79a169bd203e41,0xf0062c6e984d386, + 0xc75809c42c684dd1,0x52c07b78a3e60868, + 0xf92e0c3537826145,0xa7709a56ccdf8a82, + 0x9bbcc7a142b17ccb,0x88a66076400bb691, + 0xc2abf989935ddbfe,0x6acff893d00ea435, + 0xf356f7ebf83552fe,0x583f6b8c4124d43, + 0x98165af37b2153de,0xc3727a337a8b704a, + 0xbe1bf1b059e9a8d6,0x744f18c0592e4c5c, + 0xeda2ee1c7064130c,0x1162def06f79df73, + 0x9485d4d1c63e8be7,0x8addcb5645ac2ba8, + 0xb9a74a0637ce2ee1,0x6d953e2bd7173692, + 0xe8111c87c5c1ba99,0xc8fa8db6ccdd0437, + 0x910ab1d4db9914a0,0x1d9c9892400a22a2, + 0xb54d5e4a127f59c8,0x2503beb6d00cab4b, + 0xe2a0b5dc971f303a,0x2e44ae64840fd61d, + 0x8da471a9de737e24,0x5ceaecfed289e5d2, + 0xb10d8e1456105dad,0x7425a83e872c5f47, + 0xdd50f1996b947518,0xd12f124e28f77719, + 0x8a5296ffe33cc92f,0x82bd6b70d99aaa6f, + 0xace73cbfdc0bfb7b,0x636cc64d1001550b, + 0xd8210befd30efa5a,0x3c47f7e05401aa4e, + 0x8714a775e3e95c78,0x65acfaec34810a71, + 0xa8d9d1535ce3b396,0x7f1839a741a14d0d, + 0xd31045a8341ca07c,0x1ede48111209a050, + 0x83ea2b892091e44d,0x934aed0aab460432, + 0xa4e4b66b68b65d60,0xf81da84d5617853f, + 0xce1de40642e3f4b9,0x36251260ab9d668e, + 0x80d2ae83e9ce78f3,0xc1d72b7c6b426019, + 0xa1075a24e4421730,0xb24cf65b8612f81f, + 0xc94930ae1d529cfc,0xdee033f26797b627, + 0xfb9b7cd9a4a7443c,0x169840ef017da3b1, + 0x9d412e0806e88aa5,0x8e1f289560ee864e, + 0xc491798a08a2ad4e,0xf1a6f2bab92a27e2, + 0xf5b5d7ec8acb58a2,0xae10af696774b1db, + 0x9991a6f3d6bf1765,0xacca6da1e0a8ef29, + 0xbff610b0cc6edd3f,0x17fd090a58d32af3, + 0xeff394dcff8a948e,0xddfc4b4cef07f5b0, + 0x95f83d0a1fb69cd9,0x4abdaf101564f98e, + 0xbb764c4ca7a4440f,0x9d6d1ad41abe37f1, + 0xea53df5fd18d5513,0x84c86189216dc5ed, + 0x92746b9be2f8552c,0x32fd3cf5b4e49bb4, + 0xb7118682dbb66a77,0x3fbc8c33221dc2a1, + 0xe4d5e82392a40515,0xfabaf3feaa5334a, + 0x8f05b1163ba6832d,0x29cb4d87f2a7400e, + 0xb2c71d5bca9023f8,0x743e20e9ef511012, + 0xdf78e4b2bd342cf6,0x914da9246b255416, + 0x8bab8eefb6409c1a,0x1ad089b6c2f7548e, + 0xae9672aba3d0c320,0xa184ac2473b529b1, + 0xda3c0f568cc4f3e8,0xc9e5d72d90a2741e, + 0x8865899617fb1871,0x7e2fa67c7a658892, + 0xaa7eebfb9df9de8d,0xddbb901b98feeab7, + 0xd51ea6fa85785631,0x552a74227f3ea565, + 0x8533285c936b35de,0xd53a88958f87275f, + 0xa67ff273b8460356,0x8a892abaf368f137, + 0xd01fef10a657842c,0x2d2b7569b0432d85, + 0x8213f56a67f6b29b,0x9c3b29620e29fc73, + 0xa298f2c501f45f42,0x8349f3ba91b47b8f, + 0xcb3f2f7642717713,0x241c70a936219a73, + 0xfe0efb53d30dd4d7,0xed238cd383aa0110, + 0x9ec95d1463e8a506,0xf4363804324a40aa, + 0xc67bb4597ce2ce48,0xb143c6053edcd0d5, + 0xf81aa16fdc1b81da,0xdd94b7868e94050a, + 0x9b10a4e5e9913128,0xca7cf2b4191c8326, + 0xc1d4ce1f63f57d72,0xfd1c2f611f63a3f0, + 0xf24a01a73cf2dccf,0xbc633b39673c8cec, + 0x976e41088617ca01,0xd5be0503e085d813, + 0xbd49d14aa79dbc82,0x4b2d8644d8a74e18, + 0xec9c459d51852ba2,0xddf8e7d60ed1219e, + 0x93e1ab8252f33b45,0xcabb90e5c942b503, + 0xb8da1662e7b00a17,0x3d6a751f3b936243, + 0xe7109bfba19c0c9d,0xcc512670a783ad4, + 0x906a617d450187e2,0x27fb2b80668b24c5, + 0xb484f9dc9641e9da,0xb1f9f660802dedf6, + 0xe1a63853bbd26451,0x5e7873f8a0396973, + 0x8d07e33455637eb2,0xdb0b487b6423e1e8, + 0xb049dc016abc5e5f,0x91ce1a9a3d2cda62, + 0xdc5c5301c56b75f7,0x7641a140cc7810fb, + 0x89b9b3e11b6329ba,0xa9e904c87fcb0a9d, + 0xac2820d9623bf429,0x546345fa9fbdcd44, + 0xd732290fbacaf133,0xa97c177947ad4095, + 0x867f59a9d4bed6c0,0x49ed8eabcccc485d, + 0xa81f301449ee8c70,0x5c68f256bfff5a74, + 0xd226fc195c6a2f8c,0x73832eec6fff3111, + 0x83585d8fd9c25db7,0xc831fd53c5ff7eab, + 0xa42e74f3d032f525,0xba3e7ca8b77f5e55, + 0xcd3a1230c43fb26f,0x28ce1bd2e55f35eb, + 0x80444b5e7aa7cf85,0x7980d163cf5b81b3, + 0xa0555e361951c366,0xd7e105bcc332621f, + 0xc86ab5c39fa63440,0x8dd9472bf3fefaa7, + 0xfa856334878fc150,0xb14f98f6f0feb951, + 0x9c935e00d4b9d8d2,0x6ed1bf9a569f33d3, + 0xc3b8358109e84f07,0xa862f80ec4700c8, + 0xf4a642e14c6262c8,0xcd27bb612758c0fa, + 0x98e7e9cccfbd7dbd,0x8038d51cb897789c, + 0xbf21e44003acdd2c,0xe0470a63e6bd56c3, + 0xeeea5d5004981478,0x1858ccfce06cac74, + 0x95527a5202df0ccb,0xf37801e0c43ebc8, + 0xbaa718e68396cffd,0xd30560258f54e6ba, + 0xe950df20247c83fd,0x47c6b82ef32a2069, + 0x91d28b7416cdd27e,0x4cdc331d57fa5441, + 0xb6472e511c81471d,0xe0133fe4adf8e952, + 0xe3d8f9e563a198e5,0x58180fddd97723a6, + 0x8e679c2f5e44ff8f,0x570f09eaa7ea7648,}; +using powers = powers_template<>; + +} + +#endif + +#ifndef FASTFLOAT_DECIMAL_TO_BINARY_H +#define FASTFLOAT_DECIMAL_TO_BINARY_H + +namespace fast_float { + +// This will compute or rather approximate w * 5**q and return a pair of 64-bit words approximating +// the result, with the "high" part corresponding to the most significant bits and the +// low part corresponding to the least significant bits. +// +template +fastfloat_really_inline +value128 compute_product_approximation(int64_t q, uint64_t w) { + const int index = 2 * int(q - powers::smallest_power_of_five); + // For small values of q, e.g., q in [0,27], the answer is always exact because + // The line value128 firstproduct = full_multiplication(w, power_of_five_128[index]); + // gives the exact answer. + value128 firstproduct = full_multiplication(w, powers::power_of_five_128[index]); + static_assert((bit_precision >= 0) && (bit_precision <= 64), " precision should be in (0,64]"); + constexpr uint64_t precision_mask = (bit_precision < 64) ? + (uint64_t(0xFFFFFFFFFFFFFFFF) >> bit_precision) + : uint64_t(0xFFFFFFFFFFFFFFFF); + if((firstproduct.high & precision_mask) == precision_mask) { // could further guard with (lower + w < lower) + // regarding the second product, we only need secondproduct.high, but our expectation is that the compiler will optimize this extra work away if needed. + value128 secondproduct = full_multiplication(w, powers::power_of_five_128[index + 1]); + firstproduct.low += secondproduct.high; + if(secondproduct.high > firstproduct.low) { + firstproduct.high++; + } + } + return firstproduct; +} + +namespace detail { +/** + * For q in (0,350), we have that + * f = (((152170 + 65536) * q ) >> 16); + * is equal to + * floor(p) + q + * where + * p = log(5**q)/log(2) = q * log(5)/log(2) + * + * For negative values of q in (-400,0), we have that + * f = (((152170 + 65536) * q ) >> 16); + * is equal to + * -ceil(p) + q + * where + * p = log(5**-q)/log(2) = -q * log(5)/log(2) + */ + constexpr fastfloat_really_inline int32_t power(int32_t q) noexcept { + return (((152170 + 65536) * q) >> 16) + 63; + } +} // namespace detail + +// create an adjusted mantissa, biased by the invalid power2 +// for significant digits already multiplied by 10 ** q. +template +fastfloat_really_inline +adjusted_mantissa compute_error_scaled(int64_t q, uint64_t w, int lz) noexcept { + int hilz = int(w >> 63) ^ 1; + adjusted_mantissa answer; + answer.mantissa = w << hilz; + int bias = binary::mantissa_explicit_bits() - binary::minimum_exponent(); + answer.power2 = int32_t(detail::power(int32_t(q)) + bias - hilz - lz - 62 + invalid_am_bias); + return answer; +} + +// w * 10 ** q, without rounding the representation up. +// the power2 in the exponent will be adjusted by invalid_am_bias. +template +fastfloat_really_inline +adjusted_mantissa compute_error(int64_t q, uint64_t w) noexcept { + int lz = leading_zeroes(w); + w <<= lz; + value128 product = compute_product_approximation(q, w); + return compute_error_scaled(q, product.high, lz); +} + +// w * 10 ** q +// The returned value should be a valid ieee64 number that simply need to be packed. +// However, in some very rare cases, the computation will fail. In such cases, we +// return an adjusted_mantissa with a negative power of 2: the caller should recompute +// in such cases. +template +fastfloat_really_inline +adjusted_mantissa compute_float(int64_t q, uint64_t w) noexcept { + adjusted_mantissa answer; + if ((w == 0) || (q < binary::smallest_power_of_ten())) { + answer.power2 = 0; + answer.mantissa = 0; + // result should be zero + return answer; + } + if (q > binary::largest_power_of_ten()) { + // we want to get infinity: + answer.power2 = binary::infinite_power(); + answer.mantissa = 0; + return answer; + } + // At this point in time q is in [powers::smallest_power_of_five, powers::largest_power_of_five]. + + // We want the most significant bit of i to be 1. Shift if needed. + int lz = leading_zeroes(w); + w <<= lz; + + // The required precision is binary::mantissa_explicit_bits() + 3 because + // 1. We need the implicit bit + // 2. We need an extra bit for rounding purposes + // 3. We might lose a bit due to the "upperbit" routine (result too small, requiring a shift) + + value128 product = compute_product_approximation(q, w); + if(product.low == 0xFFFFFFFFFFFFFFFF) { // could guard it further + // In some very rare cases, this could happen, in which case we might need a more accurate + // computation that what we can provide cheaply. This is very, very unlikely. + // + const bool inside_safe_exponent = (q >= -27) && (q <= 55); // always good because 5**q <2**128 when q>=0, + // and otherwise, for q<0, we have 5**-q<2**64 and the 128-bit reciprocal allows for exact computation. + if(!inside_safe_exponent) { + return compute_error_scaled(q, product.high, lz); + } + } + // The "compute_product_approximation" function can be slightly slower than a branchless approach: + // value128 product = compute_product(q, w); + // but in practice, we can win big with the compute_product_approximation if its additional branch + // is easily predicted. Which is best is data specific. + int upperbit = int(product.high >> 63); + + answer.mantissa = product.high >> (upperbit + 64 - binary::mantissa_explicit_bits() - 3); + + answer.power2 = int32_t(detail::power(int32_t(q)) + upperbit - lz - binary::minimum_exponent()); + if (answer.power2 <= 0) { // we have a subnormal? + // Here have that answer.power2 <= 0 so -answer.power2 >= 0 + if(-answer.power2 + 1 >= 64) { // if we have more than 64 bits below the minimum exponent, you have a zero for sure. + answer.power2 = 0; + answer.mantissa = 0; + // result should be zero + return answer; + } + // next line is safe because -answer.power2 + 1 < 64 + answer.mantissa >>= -answer.power2 + 1; + // Thankfully, we can't have both "round-to-even" and subnormals because + // "round-to-even" only occurs for powers close to 0. + answer.mantissa += (answer.mantissa & 1); // round up + answer.mantissa >>= 1; + // There is a weird scenario where we don't have a subnormal but just. + // Suppose we start with 2.2250738585072013e-308, we end up + // with 0x3fffffffffffff x 2^-1023-53 which is technically subnormal + // whereas 0x40000000000000 x 2^-1023-53 is normal. Now, we need to round + // up 0x3fffffffffffff x 2^-1023-53 and once we do, we are no longer + // subnormal, but we can only know this after rounding. + // So we only declare a subnormal if we are smaller than the threshold. + answer.power2 = (answer.mantissa < (uint64_t(1) << binary::mantissa_explicit_bits())) ? 0 : 1; + return answer; + } + + // usually, we round *up*, but if we fall right in between and and we have an + // even basis, we need to round down + // We are only concerned with the cases where 5**q fits in single 64-bit word. + if ((product.low <= 1) && (q >= binary::min_exponent_round_to_even()) && (q <= binary::max_exponent_round_to_even()) && + ((answer.mantissa & 3) == 1) ) { // we may fall between two floats! + // To be in-between two floats we need that in doing + // answer.mantissa = product.high >> (upperbit + 64 - binary::mantissa_explicit_bits() - 3); + // ... we dropped out only zeroes. But if this happened, then we can go back!!! + if((answer.mantissa << (upperbit + 64 - binary::mantissa_explicit_bits() - 3)) == product.high) { + answer.mantissa &= ~uint64_t(1); // flip it so that we do not round up + } + } + + answer.mantissa += (answer.mantissa & 1); // round up + answer.mantissa >>= 1; + if (answer.mantissa >= (uint64_t(2) << binary::mantissa_explicit_bits())) { + answer.mantissa = (uint64_t(1) << binary::mantissa_explicit_bits()); + answer.power2++; // undo previous addition + } + + answer.mantissa &= ~(uint64_t(1) << binary::mantissa_explicit_bits()); + if (answer.power2 >= binary::infinite_power()) { // infinity + answer.power2 = binary::infinite_power(); + answer.mantissa = 0; + } + return answer; +} + +} // namespace fast_float + +#endif + +#ifndef FASTFLOAT_BIGINT_H +#define FASTFLOAT_BIGINT_H + +namespace fast_float { + +// the limb width: we want efficient multiplication of double the bits in +// limb, or for 64-bit limbs, at least 64-bit multiplication where we can +// extract the high and low parts efficiently. this is every 64-bit +// architecture except for sparc, which emulates 128-bit multiplication. +// we might have platforms where `CHAR_BIT` is not 8, so let's avoid +// doing `8 * sizeof(limb)`. +#if defined(FASTFLOAT_64BIT) && !defined(__sparc) +#define FASTFLOAT_64BIT_LIMB +typedef uint64_t limb; +constexpr size_t limb_bits = 64; +#else +#define FASTFLOAT_32BIT_LIMB +typedef uint32_t limb; +constexpr size_t limb_bits = 32; +#endif + +typedef span limb_span; + +// number of bits in a bigint. this needs to be at least the number +// of bits required to store the largest bigint, which is +// `log2(10**(digits + max_exp))`, or `log2(10**(767 + 342))`, or +// ~3600 bits, so we round to 4000. +constexpr size_t bigint_bits = 4000; +constexpr size_t bigint_limbs = bigint_bits / limb_bits; + +// vector-like type that is allocated on the stack. the entire +// buffer is pre-allocated, and only the length changes. +template +struct stackvec { + limb data[size]; + // we never need more than 150 limbs + uint16_t length{0}; + + stackvec() = default; + stackvec(const stackvec &) = delete; + stackvec &operator=(const stackvec &) = delete; + stackvec(stackvec &&) = delete; + stackvec &operator=(stackvec &&other) = delete; + + // create stack vector from existing limb span. + stackvec(limb_span s) { + FASTFLOAT_ASSERT(try_extend(s)); + } + + limb& operator[](size_t index) noexcept { + FASTFLOAT_DEBUG_ASSERT(index < length); + return data[index]; + } + const limb& operator[](size_t index) const noexcept { + FASTFLOAT_DEBUG_ASSERT(index < length); + return data[index]; + } + // index from the end of the container + const limb& rindex(size_t index) const noexcept { + FASTFLOAT_DEBUG_ASSERT(index < length); + size_t rindex = length - index - 1; + return data[rindex]; + } + + // set the length, without bounds checking. + void set_len(size_t len) noexcept { + length = uint16_t(len); + } + constexpr size_t len() const noexcept { + return length; + } + constexpr bool is_empty() const noexcept { + return length == 0; + } + constexpr size_t capacity() const noexcept { + return size; + } + // append item to vector, without bounds checking + void push_unchecked(limb value) noexcept { + data[length] = value; + length++; + } + // append item to vector, returning if item was added + bool try_push(limb value) noexcept { + if (len() < capacity()) { + push_unchecked(value); + return true; + } else { + return false; + } + } + // add items to the vector, from a span, without bounds checking + void extend_unchecked(limb_span s) noexcept { + limb* ptr = data + length; + ::memcpy((void*)ptr, (const void*)s.ptr, sizeof(limb) * s.len()); + set_len(len() + s.len()); + } + // try to add items to the vector, returning if items were added + bool try_extend(limb_span s) noexcept { + if (len() + s.len() <= capacity()) { + extend_unchecked(s); + return true; + } else { + return false; + } + } + // resize the vector, without bounds checking + // if the new size is longer than the vector, assign value to each + // appended item. + void resize_unchecked(size_t new_len, limb value) noexcept { + if (new_len > len()) { + size_t count = new_len - len(); + limb* first = data + len(); + limb* last = first + count; + ::std::fill(first, last, value); + set_len(new_len); + } else { + set_len(new_len); + } + } + // try to resize the vector, returning if the vector was resized. + bool try_resize(size_t new_len, limb value) noexcept { + if (new_len > capacity()) { + return false; + } else { + resize_unchecked(new_len, value); + return true; + } + } + // check if any limbs are non-zero after the given index. + // this needs to be done in reverse order, since the index + // is relative to the most significant limbs. + bool nonzero(size_t index) const noexcept { + while (index < len()) { + if (rindex(index) != 0) { + return true; + } + index++; + } + return false; + } + // normalize the big integer, so most-significant zero limbs are removed. + void normalize() noexcept { + while (len() > 0 && rindex(0) == 0) { + length--; + } + } +}; + +fastfloat_really_inline +uint64_t empty_hi64(bool& truncated) noexcept { + truncated = false; + return 0; +} + +fastfloat_really_inline +uint64_t uint64_hi64(uint64_t r0, bool& truncated) noexcept { + truncated = false; + int shl = leading_zeroes(r0); + return r0 << shl; +} + +fastfloat_really_inline +uint64_t uint64_hi64(uint64_t r0, uint64_t r1, bool& truncated) noexcept { + int shl = leading_zeroes(r0); + if (shl == 0) { + truncated = r1 != 0; + return r0; + } else { + int shr = 64 - shl; + truncated = (r1 << shl) != 0; + return (r0 << shl) | (r1 >> shr); + } +} + +fastfloat_really_inline +uint64_t uint32_hi64(uint32_t r0, bool& truncated) noexcept { + return uint64_hi64(r0, truncated); +} + +fastfloat_really_inline +uint64_t uint32_hi64(uint32_t r0, uint32_t r1, bool& truncated) noexcept { + uint64_t x0 = r0; + uint64_t x1 = r1; + return uint64_hi64((x0 << 32) | x1, truncated); +} + +fastfloat_really_inline +uint64_t uint32_hi64(uint32_t r0, uint32_t r1, uint32_t r2, bool& truncated) noexcept { + uint64_t x0 = r0; + uint64_t x1 = r1; + uint64_t x2 = r2; + return uint64_hi64(x0, (x1 << 32) | x2, truncated); +} + +// add two small integers, checking for overflow. +// we want an efficient operation. for msvc, where +// we don't have built-in intrinsics, this is still +// pretty fast. +fastfloat_really_inline +limb scalar_add(limb x, limb y, bool& overflow) noexcept { + limb z; + +// gcc and clang +#if defined(__has_builtin) + #if __has_builtin(__builtin_add_overflow) + overflow = __builtin_add_overflow(x, y, &z); + return z; + #endif +#endif + + // generic, this still optimizes correctly on MSVC. + z = x + y; + overflow = z < x; + return z; +} + +// multiply two small integers, getting both the high and low bits. +fastfloat_really_inline +limb scalar_mul(limb x, limb y, limb& carry) noexcept { +#ifdef FASTFLOAT_64BIT_LIMB + #if defined(__SIZEOF_INT128__) + // GCC and clang both define it as an extension. + __uint128_t z = __uint128_t(x) * __uint128_t(y) + __uint128_t(carry); + carry = limb(z >> limb_bits); + return limb(z); + #else + // fallback, no native 128-bit integer multiplication with carry. + // on msvc, this optimizes identically, somehow. + value128 z = full_multiplication(x, y); + bool overflow; + z.low = scalar_add(z.low, carry, overflow); + z.high += uint64_t(overflow); // cannot overflow + carry = z.high; + return z.low; + #endif +#else + uint64_t z = uint64_t(x) * uint64_t(y) + uint64_t(carry); + carry = limb(z >> limb_bits); + return limb(z); +#endif +} + +// add scalar value to bigint starting from offset. +// used in grade school multiplication +template +inline bool small_add_from(stackvec& vec, limb y, size_t start) noexcept { + size_t index = start; + limb carry = y; + bool overflow; + while (carry != 0 && index < vec.len()) { + vec[index] = scalar_add(vec[index], carry, overflow); + carry = limb(overflow); + index += 1; + } + if (carry != 0) { + FASTFLOAT_TRY(vec.try_push(carry)); + } + return true; +} + +// add scalar value to bigint. +template +fastfloat_really_inline bool small_add(stackvec& vec, limb y) noexcept { + return small_add_from(vec, y, 0); +} + +// multiply bigint by scalar value. +template +inline bool small_mul(stackvec& vec, limb y) noexcept { + limb carry = 0; + for (size_t index = 0; index < vec.len(); index++) { + vec[index] = scalar_mul(vec[index], y, carry); + } + if (carry != 0) { + FASTFLOAT_TRY(vec.try_push(carry)); + } + return true; +} + +// add bigint to bigint starting from index. +// used in grade school multiplication +template +bool large_add_from(stackvec& x, limb_span y, size_t start) noexcept { + // the effective x buffer is from `xstart..x.len()`, so exit early + // if we can't get that current range. + if (x.len() < start || y.len() > x.len() - start) { + FASTFLOAT_TRY(x.try_resize(y.len() + start, 0)); + } + + bool carry = false; + for (size_t index = 0; index < y.len(); index++) { + limb xi = x[index + start]; + limb yi = y[index]; + bool c1 = false; + bool c2 = false; + xi = scalar_add(xi, yi, c1); + if (carry) { + xi = scalar_add(xi, 1, c2); + } + x[index + start] = xi; + carry = c1 | c2; + } + + // handle overflow + if (carry) { + FASTFLOAT_TRY(small_add_from(x, 1, y.len() + start)); + } + return true; +} + +// add bigint to bigint. +template +fastfloat_really_inline bool large_add_from(stackvec& x, limb_span y) noexcept { + return large_add_from(x, y, 0); +} + +// grade-school multiplication algorithm +template +bool long_mul(stackvec& x, limb_span y) noexcept { + limb_span xs = limb_span(x.data, x.len()); + stackvec z(xs); + limb_span zs = limb_span(z.data, z.len()); + + if (y.len() != 0) { + limb y0 = y[0]; + FASTFLOAT_TRY(small_mul(x, y0)); + for (size_t index = 1; index < y.len(); index++) { + limb yi = y[index]; + stackvec zi; + if (yi != 0) { + // re-use the same buffer throughout + zi.set_len(0); + FASTFLOAT_TRY(zi.try_extend(zs)); + FASTFLOAT_TRY(small_mul(zi, yi)); + limb_span zis = limb_span(zi.data, zi.len()); + FASTFLOAT_TRY(large_add_from(x, zis, index)); + } + } + } + + x.normalize(); + return true; +} + +// grade-school multiplication algorithm +template +bool large_mul(stackvec& x, limb_span y) noexcept { + if (y.len() == 1) { + FASTFLOAT_TRY(small_mul(x, y[0])); + } else { + FASTFLOAT_TRY(long_mul(x, y)); + } + return true; +} + +// big integer type. implements a small subset of big integer +// arithmetic, using simple algorithms since asymptotically +// faster algorithms are slower for a small number of limbs. +// all operations assume the big-integer is normalized. +struct bigint { + // storage of the limbs, in little-endian order. + stackvec vec; + + bigint(): vec() {} + bigint(const bigint &) = delete; + bigint &operator=(const bigint &) = delete; + bigint(bigint &&) = delete; + bigint &operator=(bigint &&other) = delete; + + bigint(uint64_t value): vec() { +#ifdef FASTFLOAT_64BIT_LIMB + vec.push_unchecked(value); +#else + vec.push_unchecked(uint32_t(value)); + vec.push_unchecked(uint32_t(value >> 32)); +#endif + vec.normalize(); + } + + // get the high 64 bits from the vector, and if bits were truncated. + // this is to get the significant digits for the float. + uint64_t hi64(bool& truncated) const noexcept { +#ifdef FASTFLOAT_64BIT_LIMB + if (vec.len() == 0) { + return empty_hi64(truncated); + } else if (vec.len() == 1) { + return uint64_hi64(vec.rindex(0), truncated); + } else { + uint64_t result = uint64_hi64(vec.rindex(0), vec.rindex(1), truncated); + truncated |= vec.nonzero(2); + return result; + } +#else + if (vec.len() == 0) { + return empty_hi64(truncated); + } else if (vec.len() == 1) { + return uint32_hi64(vec.rindex(0), truncated); + } else if (vec.len() == 2) { + return uint32_hi64(vec.rindex(0), vec.rindex(1), truncated); + } else { + uint64_t result = uint32_hi64(vec.rindex(0), vec.rindex(1), vec.rindex(2), truncated); + truncated |= vec.nonzero(3); + return result; + } +#endif + } + + // compare two big integers, returning the large value. + // assumes both are normalized. if the return value is + // negative, other is larger, if the return value is + // positive, this is larger, otherwise they are equal. + // the limbs are stored in little-endian order, so we + // must compare the limbs in ever order. + int compare(const bigint& other) const noexcept { + if (vec.len() > other.vec.len()) { + return 1; + } else if (vec.len() < other.vec.len()) { + return -1; + } else { + for (size_t index = vec.len(); index > 0; index--) { + limb xi = vec[index - 1]; + limb yi = other.vec[index - 1]; + if (xi > yi) { + return 1; + } else if (xi < yi) { + return -1; + } + } + return 0; + } + } + + // shift left each limb n bits, carrying over to the new limb + // returns true if we were able to shift all the digits. + bool shl_bits(size_t n) noexcept { + // Internally, for each item, we shift left by n, and add the previous + // right shifted limb-bits. + // For example, we transform (for u8) shifted left 2, to: + // b10100100 b01000010 + // b10 b10010001 b00001000 + FASTFLOAT_DEBUG_ASSERT(n != 0); + FASTFLOAT_DEBUG_ASSERT(n < sizeof(limb) * 8); + + size_t shl = n; + size_t shr = limb_bits - shl; + limb prev = 0; + for (size_t index = 0; index < vec.len(); index++) { + limb xi = vec[index]; + vec[index] = (xi << shl) | (prev >> shr); + prev = xi; + } + + limb carry = prev >> shr; + if (carry != 0) { + return vec.try_push(carry); + } + return true; + } + + // move the limbs left by `n` limbs. + bool shl_limbs(size_t n) noexcept { + FASTFLOAT_DEBUG_ASSERT(n != 0); + if (n + vec.len() > vec.capacity()) { + return false; + } else if (!vec.is_empty()) { + // move limbs + limb* dst = vec.data + n; + const limb* src = vec.data; + ::memmove(dst, src, sizeof(limb) * vec.len()); + // fill in empty limbs + limb* first = vec.data; + limb* last = first + n; + ::std::fill(first, last, 0); + vec.set_len(n + vec.len()); + return true; + } else { + return true; + } + } + + // move the limbs left by `n` bits. + bool shl(size_t n) noexcept { + size_t rem = n % limb_bits; + size_t div = n / limb_bits; + if (rem != 0) { + FASTFLOAT_TRY(shl_bits(rem)); + } + if (div != 0) { + FASTFLOAT_TRY(shl_limbs(div)); + } + return true; + } + + // get the number of leading zeros in the bigint. + int ctlz() const noexcept { + if (vec.is_empty()) { + return 0; + } else { +#ifdef FASTFLOAT_64BIT_LIMB + return leading_zeroes(vec.rindex(0)); +#else + // no use defining a specialized leading_zeroes for a 32-bit type. + uint64_t r0 = vec.rindex(0); + return leading_zeroes(r0 << 32); +#endif + } + } + + // get the number of bits in the bigint. + int bit_length() const noexcept { + int lz = ctlz(); + return int(limb_bits * vec.len()) - lz; + } + + bool mul(limb y) noexcept { + return small_mul(vec, y); + } + + bool add(limb y) noexcept { + return small_add(vec, y); + } + + // multiply as if by 2 raised to a power. + bool pow2(uint32_t exp) noexcept { + return shl(exp); + } + + // multiply as if by 5 raised to a power. + bool pow5(uint32_t exp) noexcept { + // multiply by a power of 5 + static constexpr uint32_t large_step = 135; + static constexpr uint64_t small_power_of_5[] = { + 1UL, 5UL, 25UL, 125UL, 625UL, 3125UL, 15625UL, 78125UL, 390625UL, + 1953125UL, 9765625UL, 48828125UL, 244140625UL, 1220703125UL, + 6103515625UL, 30517578125UL, 152587890625UL, 762939453125UL, + 3814697265625UL, 19073486328125UL, 95367431640625UL, 476837158203125UL, + 2384185791015625UL, 11920928955078125UL, 59604644775390625UL, + 298023223876953125UL, 1490116119384765625UL, 7450580596923828125UL, + }; +#ifdef FASTFLOAT_64BIT_LIMB + constexpr static limb large_power_of_5[] = { + 1414648277510068013UL, 9180637584431281687UL, 4539964771860779200UL, + 10482974169319127550UL, 198276706040285095UL}; +#else + constexpr static limb large_power_of_5[] = { + 4279965485U, 329373468U, 4020270615U, 2137533757U, 4287402176U, + 1057042919U, 1071430142U, 2440757623U, 381945767U, 46164893U}; +#endif + size_t large_length = sizeof(large_power_of_5) / sizeof(limb); + limb_span large = limb_span(large_power_of_5, large_length); + while (exp >= large_step) { + FASTFLOAT_TRY(large_mul(vec, large)); + exp -= large_step; + } +#ifdef FASTFLOAT_64BIT_LIMB + uint32_t small_step = 27; + limb max_native = 7450580596923828125UL; +#else + uint32_t small_step = 13; + limb max_native = 1220703125U; +#endif + while (exp >= small_step) { + FASTFLOAT_TRY(small_mul(vec, max_native)); + exp -= small_step; + } + if (exp != 0) { + FASTFLOAT_TRY(small_mul(vec, limb(small_power_of_5[exp]))); + } + + return true; + } + + // multiply as if by 10 raised to a power. + bool pow10(uint32_t exp) noexcept { + FASTFLOAT_TRY(pow5(exp)); + return pow2(exp); + } +}; + +} // namespace fast_float + +#endif + +#ifndef FASTFLOAT_ASCII_NUMBER_H +#define FASTFLOAT_ASCII_NUMBER_H + +namespace fast_float { + +// Next function can be micro-optimized, but compilers are entirely +// able to optimize it well. +fastfloat_really_inline bool is_integer(char c) noexcept { return c >= '0' && c <= '9'; } + +fastfloat_really_inline uint64_t byteswap(uint64_t val) { + return (val & 0xFF00000000000000) >> 56 + | (val & 0x00FF000000000000) >> 40 + | (val & 0x0000FF0000000000) >> 24 + | (val & 0x000000FF00000000) >> 8 + | (val & 0x00000000FF000000) << 8 + | (val & 0x0000000000FF0000) << 24 + | (val & 0x000000000000FF00) << 40 + | (val & 0x00000000000000FF) << 56; +} + +fastfloat_really_inline uint64_t read_u64(const char *chars) { + uint64_t val; + ::memcpy(&val, chars, sizeof(uint64_t)); +#if FASTFLOAT_IS_BIG_ENDIAN == 1 + // Need to read as-if the number was in little-endian order. + val = byteswap(val); +#endif + return val; +} + +fastfloat_really_inline void write_u64(uint8_t *chars, uint64_t val) { +#if FASTFLOAT_IS_BIG_ENDIAN == 1 + // Need to read as-if the number was in little-endian order. + val = byteswap(val); +#endif + ::memcpy(chars, &val, sizeof(uint64_t)); +} + +// credit @aqrit +fastfloat_really_inline uint32_t parse_eight_digits_unrolled(uint64_t val) { + const uint64_t mask = 0x000000FF000000FF; + const uint64_t mul1 = 0x000F424000000064; // 100 + (1000000ULL << 32) + const uint64_t mul2 = 0x0000271000000001; // 1 + (10000ULL << 32) + val -= 0x3030303030303030; + val = (val * 10) + (val >> 8); // val = (val * 2561) >> 8; + val = (((val & mask) * mul1) + (((val >> 16) & mask) * mul2)) >> 32; + return uint32_t(val); +} + +fastfloat_really_inline uint32_t parse_eight_digits_unrolled(const char *chars) noexcept { + return parse_eight_digits_unrolled(read_u64(chars)); +} + +// credit @aqrit +fastfloat_really_inline bool is_made_of_eight_digits_fast(uint64_t val) noexcept { + return !((((val + 0x4646464646464646) | (val - 0x3030303030303030)) & + 0x8080808080808080)); +} + +fastfloat_really_inline bool is_made_of_eight_digits_fast(const char *chars) noexcept { + return is_made_of_eight_digits_fast(read_u64(chars)); +} + +typedef span byte_span; + +struct parsed_number_string { + int64_t exponent{0}; + uint64_t mantissa{0}; + const char *lastmatch{nullptr}; + bool negative{false}; + bool valid{false}; + bool too_many_digits{false}; + // contains the range of the significant digits + byte_span integer{}; // non-nullable + byte_span fraction{}; // nullable +}; + +// Assuming that you use no more than 19 digits, this will +// parse an ASCII string. +fastfloat_really_inline +parsed_number_string parse_number_string(const char *p, const char *pend, parse_options options) noexcept { + const chars_format fmt = options.format; + const char decimal_point = options.decimal_point; + + parsed_number_string answer; + answer.valid = false; + answer.too_many_digits = false; + answer.negative = (*p == '-'); + if (*p == '-') { // C++17 20.19.3.(7.1) explicitly forbids '+' sign here + ++p; + if (p == pend) { + return answer; + } + if (!is_integer(*p) && (*p != decimal_point)) { // a sign must be followed by an integer or the dot + return answer; + } + } + const char *const start_digits = p; + + uint64_t i = 0; // an unsigned int avoids signed overflows (which are bad) + + while ((std::distance(p, pend) >= 8) && is_made_of_eight_digits_fast(p)) { + i = i * 100000000 + parse_eight_digits_unrolled(p); // in rare cases, this will overflow, but that's ok + p += 8; + } + while ((p != pend) && is_integer(*p)) { + // a multiplication by 10 is cheaper than an arbitrary integer + // multiplication + i = 10 * i + + uint64_t(*p - '0'); // might overflow, we will handle the overflow later + ++p; + } + const char *const end_of_integer_part = p; + int64_t digit_count = int64_t(end_of_integer_part - start_digits); + answer.integer = byte_span(start_digits, size_t(digit_count)); + int64_t exponent = 0; + if ((p != pend) && (*p == decimal_point)) { + ++p; + const char* before = p; + // can occur at most twice without overflowing, but let it occur more, since + // for integers with many digits, digit parsing is the primary bottleneck. + while ((std::distance(p, pend) >= 8) && is_made_of_eight_digits_fast(p)) { + i = i * 100000000 + parse_eight_digits_unrolled(p); // in rare cases, this will overflow, but that's ok + p += 8; + } + while ((p != pend) && is_integer(*p)) { + uint8_t digit = uint8_t(*p - '0'); + ++p; + i = i * 10 + digit; // in rare cases, this will overflow, but that's ok + } + exponent = before - p; + answer.fraction = byte_span(before, size_t(p - before)); + digit_count -= exponent; + } + // we must have encountered at least one integer! + if (digit_count == 0) { + return answer; + } + int64_t exp_number = 0; // explicit exponential part + if ((fmt & chars_format::scientific) && (p != pend) && (('e' == *p) || ('E' == *p))) { + const char * location_of_e = p; + ++p; + bool neg_exp = false; + if ((p != pend) && ('-' == *p)) { + neg_exp = true; + ++p; + } else if ((p != pend) && ('+' == *p)) { // '+' on exponent is allowed by C++17 20.19.3.(7.1) + ++p; + } + if ((p == pend) || !is_integer(*p)) { + if(!(fmt & chars_format::fixed)) { + // We are in error. + return answer; + } + // Otherwise, we will be ignoring the 'e'. + p = location_of_e; + } else { + while ((p != pend) && is_integer(*p)) { + uint8_t digit = uint8_t(*p - '0'); + if (exp_number < 0x10000000) { + exp_number = 10 * exp_number + digit; + } + ++p; + } + if(neg_exp) { exp_number = - exp_number; } + exponent += exp_number; + } + } else { + // If it scientific and not fixed, we have to bail out. + if((fmt & chars_format::scientific) && !(fmt & chars_format::fixed)) { return answer; } + } + answer.lastmatch = p; + answer.valid = true; + + // If we frequently had to deal with long strings of digits, + // we could extend our code by using a 128-bit integer instead + // of a 64-bit integer. However, this is uncommon. + // + // We can deal with up to 19 digits. + if (digit_count > 19) { // this is uncommon + // It is possible that the integer had an overflow. + // We have to handle the case where we have 0.0000somenumber. + // We need to be mindful of the case where we only have zeroes... + // E.g., 0.000000000...000. + const char *start = start_digits; + while ((start != pend) && (*start == '0' || *start == decimal_point)) { + if(*start == '0') { digit_count --; } + start++; + } + if (digit_count > 19) { + answer.too_many_digits = true; + // Let us start again, this time, avoiding overflows. + // We don't need to check if is_integer, since we use the + // pre-tokenized spans from above. + i = 0; + p = answer.integer.ptr; + const char* int_end = p + answer.integer.len(); + const uint64_t minimal_nineteen_digit_integer{1000000000000000000}; + while((i < minimal_nineteen_digit_integer) && (p != int_end)) { + i = i * 10 + uint64_t(*p - '0'); + ++p; + } + if (i >= minimal_nineteen_digit_integer) { // We have a big integers + exponent = end_of_integer_part - p + exp_number; + } else { // We have a value with a fractional component. + p = answer.fraction.ptr; + const char* frac_end = p + answer.fraction.len(); + while((i < minimal_nineteen_digit_integer) && (p != frac_end)) { + i = i * 10 + uint64_t(*p - '0'); + ++p; + } + exponent = answer.fraction.ptr - p + exp_number; + } + // We have now corrected both exponent and i, to a truncated value + } + } + answer.exponent = exponent; + answer.mantissa = i; + return answer; +} + +} // namespace fast_float + +#endif + +#ifndef FASTFLOAT_DIGIT_COMPARISON_H +#define FASTFLOAT_DIGIT_COMPARISON_H + +namespace fast_float { + +// 1e0 to 1e19 +constexpr static uint64_t powers_of_ten_uint64[] = { + 1UL, 10UL, 100UL, 1000UL, 10000UL, 100000UL, 1000000UL, 10000000UL, 100000000UL, + 1000000000UL, 10000000000UL, 100000000000UL, 1000000000000UL, 10000000000000UL, + 100000000000000UL, 1000000000000000UL, 10000000000000000UL, 100000000000000000UL, + 1000000000000000000UL, 10000000000000000000UL}; + +// calculate the exponent, in scientific notation, of the number. +// this algorithm is not even close to optimized, but it has no practical +// effect on performance: in order to have a faster algorithm, we'd need +// to slow down performance for faster algorithms, and this is still fast. +fastfloat_really_inline int32_t scientific_exponent(parsed_number_string& num) noexcept { + uint64_t mantissa = num.mantissa; + int32_t exponent = int32_t(num.exponent); + while (mantissa >= 10000) { + mantissa /= 10000; + exponent += 4; + } + while (mantissa >= 100) { + mantissa /= 100; + exponent += 2; + } + while (mantissa >= 10) { + mantissa /= 10; + exponent += 1; + } + return exponent; +} + +// this converts a native floating-point number to an extended-precision float. +template +fastfloat_really_inline adjusted_mantissa to_extended(T value) noexcept { + using equiv_uint = typename binary_format::equiv_uint; + constexpr equiv_uint exponent_mask = binary_format::exponent_mask(); + constexpr equiv_uint mantissa_mask = binary_format::mantissa_mask(); + constexpr equiv_uint hidden_bit_mask = binary_format::hidden_bit_mask(); + + adjusted_mantissa am; + int32_t bias = binary_format::mantissa_explicit_bits() - binary_format::minimum_exponent(); + equiv_uint bits; + ::memcpy(&bits, &value, sizeof(T)); + if ((bits & exponent_mask) == 0) { + // denormal + am.power2 = 1 - bias; + am.mantissa = bits & mantissa_mask; + } else { + // normal + am.power2 = int32_t((bits & exponent_mask) >> binary_format::mantissa_explicit_bits()); + am.power2 -= bias; + am.mantissa = (bits & mantissa_mask) | hidden_bit_mask; + } + + return am; +} + +// get the extended precision value of the halfway point between b and b+u. +// we are given a native float that represents b, so we need to adjust it +// halfway between b and b+u. +template +fastfloat_really_inline adjusted_mantissa to_extended_halfway(T value) noexcept { + adjusted_mantissa am = to_extended(value); + am.mantissa <<= 1; + am.mantissa += 1; + am.power2 -= 1; + return am; +} + +// round an extended-precision float to the nearest machine float. +template +fastfloat_really_inline void round(adjusted_mantissa& am, callback cb) noexcept { + int32_t mantissa_shift = 64 - binary_format::mantissa_explicit_bits() - 1; + if (-am.power2 >= mantissa_shift) { + // have a denormal float + int32_t shift = -am.power2 + 1; + cb(am, std::min(shift, 64)); + // check for round-up: if rounding-nearest carried us to the hidden bit. + am.power2 = (am.mantissa < (uint64_t(1) << binary_format::mantissa_explicit_bits())) ? 0 : 1; + return; + } + + // have a normal float, use the default shift. + cb(am, mantissa_shift); + + // check for carry + if (am.mantissa >= (uint64_t(2) << binary_format::mantissa_explicit_bits())) { + am.mantissa = (uint64_t(1) << binary_format::mantissa_explicit_bits()); + am.power2++; + } + + // check for infinite: we could have carried to an infinite power + am.mantissa &= ~(uint64_t(1) << binary_format::mantissa_explicit_bits()); + if (am.power2 >= binary_format::infinite_power()) { + am.power2 = binary_format::infinite_power(); + am.mantissa = 0; + } +} + +template +fastfloat_really_inline +void round_nearest_tie_even(adjusted_mantissa& am, int32_t shift, callback cb) noexcept { + uint64_t mask; + uint64_t halfway; + if (shift == 64) { + mask = UINT64_MAX; + } else { + mask = (uint64_t(1) << shift) - 1; + } + if (shift == 0) { + halfway = 0; + } else { + halfway = uint64_t(1) << (shift - 1); + } + uint64_t truncated_bits = am.mantissa & mask; + uint64_t is_above = truncated_bits > halfway; + uint64_t is_halfway = truncated_bits == halfway; + + // shift digits into position + if (shift == 64) { + am.mantissa = 0; + } else { + am.mantissa >>= shift; + } + am.power2 += shift; + + bool is_odd = (am.mantissa & 1) == 1; + am.mantissa += uint64_t(cb(is_odd, is_halfway, is_above)); +} + +fastfloat_really_inline void round_down(adjusted_mantissa& am, int32_t shift) noexcept { + if (shift == 64) { + am.mantissa = 0; + } else { + am.mantissa >>= shift; + } + am.power2 += shift; +} + +fastfloat_really_inline void skip_zeros(const char*& first, const char* last) noexcept { + uint64_t val; + while (std::distance(first, last) >= 8) { + ::memcpy(&val, first, sizeof(uint64_t)); + if (val != 0x3030303030303030) { + break; + } + first += 8; + } + while (first != last) { + if (*first != '0') { + break; + } + first++; + } +} + +// determine if any non-zero digits were truncated. +// all characters must be valid digits. +fastfloat_really_inline bool is_truncated(const char* first, const char* last) noexcept { + // do 8-bit optimizations, can just compare to 8 literal 0s. + uint64_t val; + while (std::distance(first, last) >= 8) { + ::memcpy(&val, first, sizeof(uint64_t)); + if (val != 0x3030303030303030) { + return true; + } + first += 8; + } + while (first != last) { + if (*first != '0') { + return true; + } + first++; + } + return false; +} + +fastfloat_really_inline bool is_truncated(byte_span s) noexcept { + return is_truncated(s.ptr, s.ptr + s.len()); +} + +fastfloat_really_inline +void parse_eight_digits(const char*& p, limb& value, size_t& counter, size_t& count) noexcept { + value = value * 100000000 + parse_eight_digits_unrolled(p); + p += 8; + counter += 8; + count += 8; +} + +fastfloat_really_inline +void parse_one_digit(const char*& p, limb& value, size_t& counter, size_t& count) noexcept { + value = value * 10 + limb(*p - '0'); + p++; + counter++; + count++; +} + +fastfloat_really_inline +void add_native(bigint& big, limb power, limb value) noexcept { + big.mul(power); + big.add(value); +} + +fastfloat_really_inline void round_up_bigint(bigint& big, size_t& count) noexcept { + // need to round-up the digits, but need to avoid rounding + // ....9999 to ...10000, which could cause a false halfway point. + add_native(big, 10, 1); + count++; +} + +// parse the significant digits into a big integer +inline void parse_mantissa(bigint& result, parsed_number_string& num, size_t max_digits, size_t& digits) noexcept { + // try to minimize the number of big integer and scalar multiplication. + // therefore, try to parse 8 digits at a time, and multiply by the largest + // scalar value (9 or 19 digits) for each step. + size_t counter = 0; + digits = 0; + limb value = 0; +#ifdef FASTFLOAT_64BIT_LIMB + size_t step = 19; +#else + size_t step = 9; +#endif + + // process all integer digits. + const char* p = num.integer.ptr; + const char* pend = p + num.integer.len(); + skip_zeros(p, pend); + // process all digits, in increments of step per loop + while (p != pend) { + while ((std::distance(p, pend) >= 8) && (step - counter >= 8) && (max_digits - digits >= 8)) { + parse_eight_digits(p, value, counter, digits); + } + while (counter < step && p != pend && digits < max_digits) { + parse_one_digit(p, value, counter, digits); + } + if (digits == max_digits) { + // add the temporary value, then check if we've truncated any digits + add_native(result, limb(powers_of_ten_uint64[counter]), value); + bool truncated = is_truncated(p, pend); + if (num.fraction.ptr != nullptr) { + truncated |= is_truncated(num.fraction); + } + if (truncated) { + round_up_bigint(result, digits); + } + return; + } else { + add_native(result, limb(powers_of_ten_uint64[counter]), value); + counter = 0; + value = 0; + } + } + + // add our fraction digits, if they're available. + if (num.fraction.ptr != nullptr) { + p = num.fraction.ptr; + pend = p + num.fraction.len(); + if (digits == 0) { + skip_zeros(p, pend); + } + // process all digits, in increments of step per loop + while (p != pend) { + while ((std::distance(p, pend) >= 8) && (step - counter >= 8) && (max_digits - digits >= 8)) { + parse_eight_digits(p, value, counter, digits); + } + while (counter < step && p != pend && digits < max_digits) { + parse_one_digit(p, value, counter, digits); + } + if (digits == max_digits) { + // add the temporary value, then check if we've truncated any digits + add_native(result, limb(powers_of_ten_uint64[counter]), value); + bool truncated = is_truncated(p, pend); + if (truncated) { + round_up_bigint(result, digits); + } + return; + } else { + add_native(result, limb(powers_of_ten_uint64[counter]), value); + counter = 0; + value = 0; + } + } + } + + if (counter != 0) { + add_native(result, limb(powers_of_ten_uint64[counter]), value); + } +} + +template +inline adjusted_mantissa positive_digit_comp(bigint& bigmant, int32_t exponent) noexcept { + FASTFLOAT_ASSERT(bigmant.pow10(uint32_t(exponent))); + adjusted_mantissa answer; + bool truncated; + answer.mantissa = bigmant.hi64(truncated); + int bias = binary_format::mantissa_explicit_bits() - binary_format::minimum_exponent(); + answer.power2 = bigmant.bit_length() - 64 + bias; + + round(answer, [truncated](adjusted_mantissa& a, int32_t shift) { + round_nearest_tie_even(a, shift, [truncated](bool is_odd, bool is_halfway, bool is_above) -> bool { + return is_above || (is_halfway && truncated) || (is_odd && is_halfway); + }); + }); + + return answer; +} + +// the scaling here is quite simple: we have, for the real digits `m * 10^e`, +// and for the theoretical digits `n * 2^f`. Since `e` is always negative, +// to scale them identically, we do `n * 2^f * 5^-f`, so we now have `m * 2^e`. +// we then need to scale by `2^(f- e)`, and then the two significant digits +// are of the same magnitude. +template +inline adjusted_mantissa negative_digit_comp(bigint& bigmant, adjusted_mantissa am, int32_t exponent) noexcept { + bigint& real_digits = bigmant; + int32_t real_exp = exponent; + + // get the value of `b`, rounded down, and get a bigint representation of b+h + adjusted_mantissa am_b = am; + // gcc7 buf: use a lambda to remove the noexcept qualifier bug with -Wnoexcept-type. + round(am_b, [](adjusted_mantissa&a, int32_t shift) { round_down(a, shift); }); + T b; + to_float(false, am_b, b); + adjusted_mantissa theor = to_extended_halfway(b); + bigint theor_digits(theor.mantissa); + int32_t theor_exp = theor.power2; + + // scale real digits and theor digits to be same power. + int32_t pow2_exp = theor_exp - real_exp; + uint32_t pow5_exp = uint32_t(-real_exp); + if (pow5_exp != 0) { + FASTFLOAT_ASSERT(theor_digits.pow5(pow5_exp)); + } + if (pow2_exp > 0) { + FASTFLOAT_ASSERT(theor_digits.pow2(uint32_t(pow2_exp))); + } else if (pow2_exp < 0) { + FASTFLOAT_ASSERT(real_digits.pow2(uint32_t(-pow2_exp))); + } + + // compare digits, and use it to director rounding + int ord = real_digits.compare(theor_digits); + adjusted_mantissa answer = am; + round(answer, [ord](adjusted_mantissa& a, int32_t shift) { + round_nearest_tie_even(a, shift, [ord](bool is_odd, bool _, bool __) -> bool { + (void)_; // not needed, since we've done our comparison + (void)__; // not needed, since we've done our comparison + if (ord > 0) { + return true; + } else if (ord < 0) { + return false; + } else { + return is_odd; + } + }); + }); + + return answer; +} + +// parse the significant digits as a big integer to unambiguously round the +// the significant digits. here, we are trying to determine how to round +// an extended float representation close to `b+h`, halfway between `b` +// (the float rounded-down) and `b+u`, the next positive float. this +// algorithm is always correct, and uses one of two approaches. when +// the exponent is positive relative to the significant digits (such as +// 1234), we create a big-integer representation, get the high 64-bits, +// determine if any lower bits are truncated, and use that to direct +// rounding. in case of a negative exponent relative to the significant +// digits (such as 1.2345), we create a theoretical representation of +// `b` as a big-integer type, scaled to the same binary exponent as +// the actual digits. we then compare the big integer representations +// of both, and use that to direct rounding. +template +inline adjusted_mantissa digit_comp(parsed_number_string& num, adjusted_mantissa am) noexcept { + // remove the invalid exponent bias + am.power2 -= invalid_am_bias; + + int32_t sci_exp = scientific_exponent(num); + size_t max_digits = binary_format::max_digits(); + size_t digits = 0; + bigint bigmant; + parse_mantissa(bigmant, num, max_digits, digits); + // can't underflow, since digits is at most max_digits. + int32_t exponent = sci_exp + 1 - int32_t(digits); + if (exponent >= 0) { + return positive_digit_comp(bigmant, exponent); + } else { + return negative_digit_comp(bigmant, am, exponent); + } +} + +} // namespace fast_float + +#endif + +#ifndef FASTFLOAT_PARSE_NUMBER_H +#define FASTFLOAT_PARSE_NUMBER_H + +namespace fast_float { + + +namespace detail { +/** + * Special case +inf, -inf, nan, infinity, -infinity. + * The case comparisons could be made much faster given that we know that the + * strings a null-free and fixed. + **/ +template +from_chars_result parse_infnan(const char *first, const char *last, T &value) noexcept { + from_chars_result answer; + answer.ptr = first; + answer.ec = std::errc(); // be optimistic + bool minusSign = false; + if (*first == '-') { // assume first < last, so dereference without checks; C++17 20.19.3.(7.1) explicitly forbids '+' here + minusSign = true; + ++first; + } + if (last - first >= 3) { + if (fastfloat_strncasecmp(first, "nan", 3)) { + answer.ptr = (first += 3); + value = minusSign ? -std::numeric_limits::quiet_NaN() : std::numeric_limits::quiet_NaN(); + // Check for possible nan(n-char-seq-opt), C++17 20.19.3.7, C11 7.20.1.3.3. At least MSVC produces nan(ind) and nan(snan). + if(first != last && *first == '(') { + for(const char* ptr = first + 1; ptr != last; ++ptr) { + if (*ptr == ')') { + answer.ptr = ptr + 1; // valid nan(n-char-seq-opt) + break; + } + else if(!(('a' <= *ptr && *ptr <= 'z') || ('A' <= *ptr && *ptr <= 'Z') || ('0' <= *ptr && *ptr <= '9') || *ptr == '_')) + break; // forbidden char, not nan(n-char-seq-opt) + } + } + return answer; + } + if (fastfloat_strncasecmp(first, "inf", 3)) { + if ((last - first >= 8) && fastfloat_strncasecmp(first + 3, "inity", 5)) { + answer.ptr = first + 8; + } else { + answer.ptr = first + 3; + } + value = minusSign ? -std::numeric_limits::infinity() : std::numeric_limits::infinity(); + return answer; + } + } + answer.ec = std::errc::invalid_argument; + return answer; +} + +} // namespace detail + +template +from_chars_result from_chars(const char *first, const char *last, + T &value, chars_format fmt /*= chars_format::general*/) noexcept { + return from_chars_advanced(first, last, value, parse_options{fmt}); +} + +template +from_chars_result from_chars_advanced(const char *first, const char *last, + T &value, parse_options options) noexcept { + + static_assert (std::is_same::value || std::is_same::value, "only float and double are supported"); + + + from_chars_result answer; + if (first == last) { + answer.ec = std::errc::invalid_argument; + answer.ptr = first; + return answer; + } + parsed_number_string pns = parse_number_string(first, last, options); + if (!pns.valid) { + return detail::parse_infnan(first, last, value); + } + answer.ec = std::errc(); // be optimistic + answer.ptr = pns.lastmatch; + // Next is Clinger's fast path. + if (binary_format::min_exponent_fast_path() <= pns.exponent && pns.exponent <= binary_format::max_exponent_fast_path() && pns.mantissa <=binary_format::max_mantissa_fast_path() && !pns.too_many_digits) { + value = T(pns.mantissa); + if (pns.exponent < 0) { value = value / binary_format::exact_power_of_ten(-pns.exponent); } + else { value = value * binary_format::exact_power_of_ten(pns.exponent); } + if (pns.negative) { value = -value; } + return answer; + } + adjusted_mantissa am = compute_float>(pns.exponent, pns.mantissa); + if(pns.too_many_digits && am.power2 >= 0) { + if(am != compute_float>(pns.exponent, pns.mantissa + 1)) { + am = compute_error>(pns.exponent, pns.mantissa); + } + } + // If we called compute_float>(pns.exponent, pns.mantissa) and we have an invalid power (am.power2 < 0), + // then we need to go the long way around again. This is very uncommon. + if(am.power2 < 0) { am = digit_comp(pns, am); } + to_float(pns.negative, am, value); + return answer; +} + +} // namespace fast_float + +#endif + +// clang-format on + +// +// Third-party includes End +// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +namespace rapidobj { +enum class rapidobj_errc { + Success, + ParseError, + MaterialFileError, + MaterialParseError, + MaterialNotFoundError, + MaterialRelativePathError, + AmbiguousMaterialLibraryError, + LineTooLongError, + IndexOutOfBoundsError, + InvalidArgumentsError, + TooFewIndicesError, + TooManyIndicesError, + TriangulationError, + InternalError +}; +} // namespace rapidobj + +namespace std { +template <> +struct is_error_code_enum : true_type {}; +} // namespace std + +namespace rapidobj { + +struct rapidobj_error_category : std::error_category { + const char* name() const noexcept override { return "rapidobj"; } + std::string message(int err) const override + { + switch (static_cast(err)) { + case rapidobj_errc::Success: return "No error."; + case rapidobj_errc::ParseError: return "Object file parse error."; + case rapidobj_errc::MaterialFileError: return "Material file not found."; + case rapidobj_errc::MaterialParseError: return "Material file parse error."; + case rapidobj_errc::MaterialNotFoundError: return "Material not found."; + case rapidobj_errc::MaterialRelativePathError: return "Material file path is relative."; + case rapidobj_errc::AmbiguousMaterialLibraryError: return "Ambiguous material library."; + case rapidobj_errc::LineTooLongError: return "Line too long."; + case rapidobj_errc::IndexOutOfBoundsError: return "Index out of bounds."; + case rapidobj_errc::InvalidArgumentsError: return "Invalid Arguments."; + case rapidobj_errc::TooFewIndicesError: return "Polygon has too few indices."; + case rapidobj_errc::TooManyIndicesError: return "Polygon has too many indices."; + case rapidobj_errc::TriangulationError: return "Triangulation errror."; + case rapidobj_errc::InternalError: return "Internal error."; + } + return "Unrecognised error."; + } +}; + +inline std::error_code make_error_code(rapidobj_errc err) +{ + static const rapidobj_error_category category{}; + return { static_cast(err), category }; +} + +namespace detail { + +constexpr std::size_t operator"" _GiB(unsigned long long int gibibytes) noexcept +{ + return static_cast(1024 * 1024 * 1024 * gibibytes); +} + +constexpr std::size_t operator"" _MiB(unsigned long long int mebibytes) noexcept +{ + return static_cast(1024 * 1024 * mebibytes); +} + +constexpr std::size_t operator"" _KiB(unsigned long long int kibibytes) noexcept +{ + return static_cast(1024 * kibibytes); +} + +static constexpr auto kMaxLineLength = 4_KiB; +static constexpr auto kBlockSize = 256_KiB; +static constexpr auto kMinVerticesInFace = 3; +static constexpr auto kMaxVerticesInFace = 255; +static constexpr auto kMinVerticesInLine = 2; +static constexpr auto kMaxVerticesInLine = 1000; +static constexpr auto kMinVerticesInPoint = 1; +static constexpr auto kMaxVerticesInPoint = 1000; +static constexpr auto kSingleThreadCutoff = 1_MiB; + +static constexpr auto kMergeCopyByteCost = 12; +static constexpr auto kMergeCopyIntCost = 31; +static constexpr auto kMergeFillIdCost = 32; +static constexpr auto kMergeCopyIndexCost = 186; +static constexpr auto kMergeSubdivideCost = 50000000; + +static constexpr auto kTriangulateTriangleCost = 55; +static constexpr auto kTriangulateQuadCost = 140; +static constexpr auto kTriangulatePerIndexCost = 46; +static constexpr auto kTriangulateSubdivideCost = 5000000; + +static constexpr auto kMemoryRecyclingSize = 25_MiB; + +static_assert(kMaxLineLength < kBlockSize); +static_assert(kBlockSize % 4_KiB == 0); + +static constexpr auto kSuccess = std::errc(); + +inline constexpr bool StartsWith(std::string_view text, std::string_view sv) noexcept +{ + if (text.length() < sv.length()) { + return false; + } + for (size_t i = 0; i != sv.length(); ++i) { + if (text[i] != sv[i]) { + return false; + } + } + return true; +} + +inline constexpr bool EndsWith(std::string_view text, char c) noexcept +{ + return text.length() > 0 && text.back() == c; +} + +inline void TrimLeft(std::string& text) noexcept +{ + size_t index = 0; + while (index < text.size() && (text[index] == ' ' || text[index] == '\t')) { + ++index; + } + text.erase(0, index); +} + +inline void TrimRight(std::string& text) noexcept +{ + auto index = text.size(); + while (index && (text[index - 1] == ' ' || text[index - 1] == '\t')) { + --index; + } + text.erase(index); +} + +inline void Trim(std::string& text) noexcept +{ + TrimLeft(text); + TrimRight(text); +} + +inline constexpr void TrimLeft(std::string_view& text) noexcept +{ + size_t index = 0; + while (index < text.size() && (text[index] == ' ' || text[index] == '\t')) { + ++index; + } + text.remove_prefix(index); +} + +inline constexpr void TrimRight(std::string_view& text) noexcept +{ + auto index = text.size(); + while (index && (text[index - 1] == ' ' || text[index - 1] == '\t')) { + --index; + } + text = text.substr(0, index); +} + +inline constexpr void Trim(std::string_view& text) noexcept +{ + TrimLeft(text); + TrimRight(text); +} + +enum class ApplyOffset : uint8_t { None = 0, Position = 1, Texcoord = 2, Normal = 4, All = 7 }; + +using OffsetFlags = std::underlying_type_t; + +inline bool operator&(OffsetFlags lhs, ApplyOffset rhs) noexcept +{ + return lhs & static_cast(rhs); +} + +inline OffsetFlags operator|(OffsetFlags lhs, ApplyOffset rhs) noexcept +{ + return lhs | static_cast(rhs); +} + +inline OffsetFlags operator|(ApplyOffset lhs, ApplyOffset rhs) noexcept +{ + return static_cast(lhs) | static_cast(rhs); +} + +template +struct Buffer final { + Buffer() noexcept = default; + Buffer(size_t size) noexcept : m_size(size), m_room(0), m_data(new T[size]) {} + + size_t size() const noexcept { return m_size; } + const T* data() const noexcept { return m_data.get(); } + T* data() noexcept { return m_data.get(); } + T& back() noexcept { return m_data[m_size - 1]; } + + void push_back(T value) noexcept + { + m_data[m_size] = value; + ++m_size, --m_room; + } + + T pop_back() noexcept + { + --m_size, ++m_room; + return m_data.get()[m_size]; + } + + void fill_n(size_t n, T value) + { + std::fill_n(m_data.get() + m_size, n, value); + m_size += n; + m_room -= n; + } + + void ensure_enough_room_for(size_t size) + { + if (size > m_room) { + auto cap = std::max(kInitialSize, 2 * (m_size + size)); + auto src = std::unique_ptr(std::move(m_data)); + m_data.reset(new T[cap]); + if (src) { + memcpy(m_data.get(), src.get(), m_size * sizeof(T)); + } + m_room = cap - m_size; + } + } + + private: + static constexpr size_t kInitialSize = 4096; + + size_t m_size{}; + size_t m_room{}; + std::unique_ptr m_data{}; + + static_assert(std::is_trivially_copyable_v); +}; + +struct ShapeRecord final { + struct Mesh final { + size_t index_buffer_start{}; + size_t face_buffer_start{}; + }; + struct Lines final { + size_t index_buffer_start{}; + size_t segment_buffer_start{}; + }; + struct Points final { + size_t index_buffer_start{}; + }; + std::string name{}; + Mesh mesh{}; + Lines lines{}; + Points points{}; + size_t chunk_index{}; +}; + +struct MaterialRecord final { + std::string name; + std::string line{}; + size_t line_num{}; + size_t face_buffer_start{}; +}; + +using MaterialMap = std::map; + +struct ParseMaterialsResult final { + MaterialMap material_map; + Materials materials; + Error error; +}; + +struct SmoothingRecord final { + unsigned int group_id{}; + size_t face_buffer_start{}; +}; + +struct SharedContext final { + struct Thread final { + size_t concurrency{}; + } thread; + + struct Stats final { + size_t num_positions{}; + size_t num_texcoords{}; + size_t num_normals{}; + } stats; + + struct Material final { + const MaterialLibrary* library{}; + std::filesystem::path basepath{}; + std::string library_name{}; + std::future parse_result{}; + std::mutex mutex{}; // protects library_name and parse_result + } material; + + struct Parsing final { + std::atomic_size_t thread_count{}; + std::promise completed{}; + } parsing; + + struct Merging final { + std::atomic_size_t task_index{}; + std::atomic_size_t thread_count{}; + std::promise completed{}; + rapidobj_errc error{}; + std::mutex mutex{}; // protects error + } merging; + + struct Debug final { + struct IO final { + std::vector num_requests{}; + std::vector num_bytes_read{}; + std::vector submit_time; + std::vector wait_time; + } io; + struct Parse final { + std::vector time; + std::chrono::nanoseconds total_time; + } parse; + struct Merge final { + std::chrono::nanoseconds total_time; + } merge; + } debug; +}; + +struct Chunk final { + struct Text final { + size_t line_count{}; + }; + struct Positions final { + Buffer buffer{}; + size_t count{}; + }; + struct Texcoords final { + Buffer buffer{}; + size_t count{}; + }; + struct Normals final { + Buffer buffer{}; + size_t count{}; + }; + struct Colors final { + Buffer buffer{}; + size_t count{}; + }; + struct Mesh final { + struct Indices final { + Buffer buffer; + Buffer flags; + } indices{}; + struct Faces final { + Buffer buffer; + size_t count; + } faces{}; + }; + struct Lines final { + struct Indices final { + Buffer buffer; + Buffer flags; + } indices{}; + struct Segments final { + Buffer buffer; + size_t count; + } segments{}; + }; + struct Points final { + struct Indices final { + Buffer buffer; + Buffer flags; + } indices{}; + }; + struct Shapes final { + std::vector list; + }; + struct Materials final { + std::vector list; + }; + struct Smoothing final { + std::vector list; + }; + + Text text; + Positions positions; + Texcoords texcoords; + Normals normals; + Colors colors; + Mesh mesh; + Lines lines; + Points points; + Shapes shapes; + Materials materials; + Smoothing smoothing; + Error error; +}; + +inline size_t SizeInBytes(const Chunk& chunk) noexcept +{ + auto size = size_t{ 0 }; + + size += chunk.positions.buffer.size() * sizeof(float); + size += chunk.texcoords.buffer.size() * sizeof(float); + size += chunk.normals.buffer.size() * sizeof(float); + size += chunk.colors.buffer.size() * sizeof(float); + size += chunk.mesh.indices.buffer.size() * sizeof(Index); + size += chunk.mesh.indices.flags.size() * sizeof(OffsetFlags); + size += chunk.mesh.faces.buffer.size() * sizeof(unsigned char); + size += chunk.lines.indices.buffer.size() * sizeof(Index); + size += chunk.lines.indices.flags.size() * sizeof(OffsetFlags); + size += chunk.lines.segments.buffer.size() * sizeof(int); + size += chunk.points.indices.buffer.size() * sizeof(Index); + size += chunk.points.indices.flags.size() * sizeof(OffsetFlags); + + return size; +} + +inline size_t SizeInBytes(const Mesh& mesh) noexcept +{ + auto size = size_t{ 0 }; + + size += mesh.indices.size() * sizeof(Index); + size += mesh.num_face_vertices.size() * sizeof(uint8_t); + size += mesh.material_ids.size() * sizeof(int32_t); + size += mesh.smoothing_group_ids.size() * sizeof(uint32_t); + + return size; +} + +template +struct CopyElements; + +template +struct FillElements; + +template +struct FillIds; + +template +struct FillSrc final { + T id{}; + size_t offset{}; +}; + +struct AttributeInfo final { + size_t position{}; + size_t texcoord{}; + size_t normal{}; + + AttributeInfo& operator+=(const AttributeInfo& rhs) noexcept + { + position += rhs.position; + texcoord += rhs.texcoord; + normal += rhs.normal; + return *this; + } +}; + +struct CopyIndices; + +using CopyBytes = CopyElements; +using CopyInts = CopyElements; +using CopyFloats = CopyElements; + +using FillFloats = FillElements; + +using FillMaterialIds = FillIds; +using FillSmoothingGroupIds = FillIds; + +template +struct CopyElements final { + CopyElements(T* dst, const T* src, size_t size) noexcept : m_dst(dst), m_src(src), m_size(size) {} + + auto Cost() const noexcept + { + constexpr auto cost = sizeof(T) == 1 ? kMergeCopyByteCost : kMergeCopyIntCost; + return cost * m_size; + } + + auto Execute() const noexcept + { + memcpy(m_dst, m_src, m_size * sizeof(T)); + return rapidobj_errc::Success; + } + + inline auto Subdivide(size_t num) const noexcept; + + private: + T* m_dst{}; + const T* m_src; + size_t m_size; +}; + +template +struct FillElements final { + FillElements(T* dst, T value, size_t size) noexcept : m_dst(dst), m_value(value), m_size(size) {} + + auto Cost() const noexcept + { + constexpr auto cost = sizeof(T) == 1 ? kMergeCopyByteCost : kMergeCopyIntCost; + return cost * m_size; + } + + auto Execute() const noexcept + { + std::fill_n(m_dst, m_size, m_value); + return rapidobj_errc::Success; + } + + inline auto Subdivide(size_t num) const noexcept; + + private: + T* m_dst{}; + const T m_value; + size_t m_size; +}; + +template +struct FillIds final { + FillIds(T* dst, const std::vector>& src, size_t size, size_t start) noexcept + : m_dst(dst), m_src(&src), m_size(size), m_start(start) + {} + + auto Cost() const noexcept { return kMergeFillIdCost * m_size; } + + auto Execute() const noexcept + { + auto comp = [](auto src, size_t offset) { return src.offset < offset; }; + auto it = std::lower_bound(m_src->begin(), m_src->end(), m_start, comp); + if (it == m_src->end()) { + return rapidobj_errc::InternalError; + } + + auto index = static_cast(it - m_src->begin()); + if (m_src->at(index).offset != m_start) { + --index; + } + + auto dst = m_dst; + auto size = m_size; + auto start = m_start; + + while (size > 0 && index < m_src->size() - 1) { + auto count = std::min(size, m_src->at(index + 1).offset - start); + std::fill_n(dst, count, m_src->at(index).id); + start += count; + dst += count; + size -= count; + ++index; + } + + return rapidobj_errc::Success; + } + + inline auto Subdivide(size_t num) const noexcept; + + private: + T* m_dst{}; + const std::vector>* m_src{}; + size_t m_size{}; + size_t m_start{}; +}; + +struct CopyIndices final { + CopyIndices( + Index* dst, + const Index* src, + const OffsetFlags* offset_flags, + size_t size, + AttributeInfo offset, + AttributeInfo count) noexcept + : m_dst(dst), m_src(src), m_offset_flags(offset_flags), m_size(size), m_offset(offset), m_count(count) + {} + + auto Cost() const noexcept { return kMergeCopyIndexCost * m_size; } + + auto Execute() const noexcept + { + for (size_t i = 0; i != m_size; ++i) { + auto position_index = m_src[i].position_index; + auto texcoord_index = m_src[i].texcoord_index; + auto normal_index = m_src[i].normal_index; + auto offset_flags = m_offset_flags[i]; + + bool is_out_of_bounds = false; + + if (offset_flags & ApplyOffset::Position) { + position_index += static_cast(m_offset.position); + } + is_out_of_bounds |= position_index < 0 || position_index >= static_cast(m_count.position); + + if (offset_flags & ApplyOffset::Texcoord) { + texcoord_index += static_cast(m_offset.texcoord); + is_out_of_bounds |= texcoord_index < 0; + } else { + is_out_of_bounds |= texcoord_index < -1; + } + is_out_of_bounds |= texcoord_index >= static_cast(m_count.texcoord); + + if (offset_flags & ApplyOffset::Normal) { + normal_index += static_cast(m_offset.normal); + is_out_of_bounds |= normal_index < 0; + } else { + is_out_of_bounds |= normal_index < -1; + } + is_out_of_bounds |= normal_index >= static_cast(m_count.normal); + + if (is_out_of_bounds) { + return rapidobj_errc::IndexOutOfBoundsError; + } + + m_dst[i].position_index = position_index; + m_dst[i].texcoord_index = texcoord_index; + m_dst[i].normal_index = normal_index; + } + + return rapidobj_errc::Success; + } + + inline auto Subdivide(size_t num) const noexcept; + + private: + Index* m_dst{}; + const Index* m_src{}; + const OffsetFlags* m_offset_flags{}; + size_t m_size{}; + AttributeInfo m_offset{}; + AttributeInfo m_count{}; +}; + +using MergeTask = + std::variant; +using MergeTasks = std::vector; + +template +auto CopyElements::Subdivide(size_t num) const noexcept +{ + auto begin = size_t{ 0 }; + auto tasks = MergeTasks(); + tasks.reserve(num); + for (size_t i = 0; i != num; ++i) { + auto end = (1 + i) * m_size / num; + tasks.push_back(CopyElements(m_dst + begin, m_src + begin, end - begin)); + begin = end; + } + return tasks; +} + +template +auto FillElements::Subdivide(size_t num) const noexcept +{ + auto begin = size_t{ 0 }; + auto tasks = MergeTasks(); + tasks.reserve(num); + for (size_t i = 0; i != num; ++i) { + auto end = (1 + i) * m_size / num; + tasks.push_back(FillElements(m_dst + begin, m_value, end - begin)); + begin = end; + } + return tasks; +} + +template +auto FillIds::Subdivide(size_t num) const noexcept +{ + auto begin = size_t{ 0 }; + auto tasks = MergeTasks(); + tasks.reserve(num); + for (size_t i = 0; i != num; ++i) { + auto end = (1 + i) * m_size / num; + tasks.push_back(FillIds(m_dst + begin, *m_src, end - begin, m_start + begin)); + begin = end; + } + return tasks; +} + +auto CopyIndices::Subdivide(size_t num) const noexcept +{ + auto begin = size_t{ 0 }; + auto tasks = MergeTasks(); + tasks.reserve(num); + for (size_t i = 0; i != num; ++i) { + auto end = (1 + i) * m_size / num; + tasks.push_back(CopyIndices(m_dst + begin, m_src + begin, m_offset_flags, end - begin, m_offset, m_count)); + begin = end; + } + return tasks; +} + +struct Reader { + struct ReadResult final { + std::size_t bytes_read; + std::error_code error_code; + }; + + virtual ~Reader() noexcept = default; + + virtual std::error_code ReadBlock(size_t offset, size_t size, char* buffer) = 0; + virtual ReadResult WaitForResult() = 0; + + auto NumRequests() const noexcept { return m_num_requests; } + auto SubmitTime() const noexcept { return m_submit_time; } + auto WaitTime() const noexcept { return m_wait_time; } + auto BytesRead() const noexcept { return m_bytes_read; } + auto Error() const noexcept { return m_error; } + + protected: + int m_num_requests{}; + std::chrono::nanoseconds m_submit_time{}; + std::chrono::nanoseconds m_wait_time{}; + size_t m_bytes_read{}; + std::error_code m_error{}; +}; + +namespace sys { + +#ifdef __linux__ + +inline auto AlignedAllocate(size_t size, size_t alignment) +{ + return static_cast(aligned_alloc(alignment, size)); +} + +struct AlignedDeleter final { + void operator()(void* ptr) const { free(ptr); } +}; + +class File final { + public: + File(const std::filesystem::path& filepath) + { + auto filepath_string = filepath.string(); + + if (-1 == stat(filepath_string.c_str(), &m_info)) { + m_error = std::error_code(errno, std::system_category()); + return; + } + + m_fd = open(filepath_string.c_str(), O_RDONLY); + + if (-1 == m_fd) { + m_error = std::error_code(errno, std::system_category()); + } + } + File(const File&) = delete; + File& operator=(const File&) = delete; + File(File&&) = delete; + File& operator=(File&&) = delete; + ~File() noexcept + { + if (m_fd != -1) { + close(m_fd); + } + } + + explicit operator bool() const noexcept { return m_fd != -1; } + auto handle() const noexcept { return m_fd; } + auto size() const noexcept { return static_cast(m_info.st_size); } + auto error() const noexcept { return m_error; } + + private: + int m_fd = -1; + struct stat m_info {}; + std::error_code m_error{}; +}; + +struct FileReader : Reader { + FileReader(const File& file) noexcept : m_fd{ file.handle() } {} + + std::error_code ReadBlock(size_t offset, size_t size, char* buffer) override + { + assert(buffer); + assert(m_fd != -1); + assert(std::uintptr_t(buffer) % 4096 == 0); + + ++m_num_requests; + + auto t1 = std::chrono::steady_clock::now(); + + m_offset = offset; + m_size = size; + m_buffer = buffer; + + readahead(m_fd, offset, size); + + auto t2 = std::chrono::steady_clock::now(); + + m_submit_time += t2 - t1; + + return {}; + } + + ReadResult WaitForResult() override + { + auto t1 = std::chrono::steady_clock::now(); + + auto result = pread64(m_fd, m_buffer, m_size, m_offset); + + auto t2 = std::chrono::steady_clock::now(); + + m_wait_time += t2 - t1; + + if (result < 0) { + auto error = std::error_code(errno, std::system_category()); + return ReadResult{ 0, error }; + } + + auto bytes_read = static_cast(result); + + m_bytes_read += bytes_read; + + return ReadResult{ bytes_read, std::error_code() }; + } + + private: + int m_fd = -1; + size_t m_offset{}; + size_t m_size{}; + char* m_buffer{}; +}; + +#elif _WIN32 + +inline auto AlignedAllocate(size_t size, size_t alignment) +{ + return static_cast(_aligned_malloc(size, alignment)); +} + +struct AlignedDeleter final { + void operator()(void* ptr) const { _aligned_free(ptr); } +}; + +class File final { + public: + File(const std::filesystem::path& filepath) + { + auto filepath_string = filepath.string(); + + m_handle = CreateFileA( + filepath_string.c_str(), + GENERIC_READ, + 0, + nullptr, + OPEN_EXISTING, + FILE_ATTRIBUTE_READONLY | FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED, + nullptr); + + if (m_handle == INVALID_HANDLE_VALUE) { + m_error = std::error_code(static_cast(GetLastError()), std::system_category()); + return; + } + + auto size = LARGE_INTEGER{}; + if (!GetFileSizeEx(m_handle, &size)) { + m_error = std::error_code(static_cast(GetLastError()), std::system_category()); + Close(); + return; + } + + m_size = static_cast(size.QuadPart); + } + File(const File&) = delete; + File& operator=(const File&) = delete; + File(File&&) = delete; + File& operator=(File&&) = delete; + ~File() noexcept { Close(); } + + explicit operator bool() const noexcept { return m_handle != nullptr && m_handle != INVALID_HANDLE_VALUE; } + auto handle() const noexcept { return m_handle; } + auto size() const noexcept { return m_size; } + auto error() const noexcept { return m_error; } + + private: + void Close() noexcept + { + if (m_handle != INVALID_HANDLE_VALUE) { + CloseHandle(m_handle); + m_handle = INVALID_HANDLE_VALUE; + } + } + + HANDLE m_handle{}; + size_t m_size{}; + std::error_code m_error{}; +}; + +struct FileReader : Reader { + FileReader(const File& file) + { + m_handle = CreateEventA(nullptr, FALSE, FALSE, nullptr); + + if (m_handle == INVALID_HANDLE_VALUE) { + m_error = std::error_code(static_cast(GetLastError()), std::system_category()); + } + + m_file = file.handle(); + } + + ~FileReader() noexcept override + { + if (m_handle != INVALID_HANDLE_VALUE) { + CloseHandle(m_handle); + } + } + + std::error_code ReadBlock(size_t offset, size_t size, char* buffer) override + { + assert(m_handle && m_handle != INVALID_HANDLE_VALUE); + assert(m_file && m_file != INVALID_HANDLE_VALUE); + + ++m_num_requests; + + auto t1 = std::chrono::steady_clock::now(); + + m_overlapped.hEvent = m_handle; + m_overlapped.Offset = static_cast(offset); + m_overlapped.OffsetHigh = static_cast(offset >> 32); + + auto error = std::error_code(); + bool success = ReadFile(m_file, buffer, static_cast(size), nullptr, &m_overlapped); + + if (!success) { + if (auto ec = GetLastError(); ec != ERROR_IO_PENDING) { + error = std::error_code(ec, std::system_category()); + } + } + + auto t2 = std::chrono::steady_clock::now(); + + m_submit_time += t2 - t1; + + return error; + } + + ReadResult WaitForResult() override + { + assert(m_handle && m_handle != INVALID_HANDLE_VALUE); + assert(m_file && m_file != INVALID_HANDLE_VALUE); + + auto t1 = std::chrono::steady_clock::now(); + + auto bytes_read = DWORD{}; + bool success = GetOverlappedResult(m_handle, &m_overlapped, &bytes_read, TRUE); + + auto t2 = std::chrono::steady_clock::now(); + + m_wait_time += t2 - t1; + + if (!success) { + if (auto ec = GetLastError(); ec != ERROR_HANDLE_EOF) { + auto error = std::error_code(ec, std::system_category()); + return ReadResult{ 0, error }; + } + } + + m_bytes_read += bytes_read; + + return { bytes_read, std::error_code() }; + } + + private: + HANDLE m_handle{}; + HANDLE m_file{}; + OVERLAPPED m_overlapped{}; +}; + +#elif __APPLE__ + +inline auto AlignedAllocate(size_t size, size_t alignment) +{ + return static_cast(aligned_alloc(alignment, size)); +} + +struct AlignedDeleter final { + void operator()(void* ptr) const { free(ptr); } +}; + +class File final { + public: + File(const std::filesystem::path& filepath) + { + auto filepath_string = filepath.string(); + + if (-1 == stat(filepath_string.c_str(), &m_info)) { + m_error = std::error_code(errno, std::system_category()); + return; + } + + m_fd = open(filepath_string.c_str(), O_RDONLY); + + if (-1 == m_fd) { + m_error = std::error_code(errno, std::system_category()); + } + } + File(const File&) = delete; + File& operator=(const File&) = delete; + File(File&&) = delete; + File& operator=(File&&) = delete; + ~File() noexcept + { + if (m_fd != -1) { + close(m_fd); + } + } + + explicit operator bool() const noexcept { return m_fd != -1; } + auto handle() const noexcept { return m_fd; } + auto size() const noexcept { return static_cast(m_info.st_size); } + auto error() const noexcept { return m_error; } + + private: + int m_fd = -1; + struct stat m_info {}; + std::error_code m_error{}; +}; + +struct FileReader : Reader { + FileReader(const File& file) noexcept : m_fd{ file.handle() } + { + assert(m_fd != -1); + + fcntl(m_fd, F_NOCACHE, 1); + } + + std::error_code ReadBlock(size_t offset, size_t size, char* buffer) override + { + assert(buffer); + assert(std::uintptr_t(buffer) % 4096 == 0); + + ++m_num_requests; + + auto t1 = std::chrono::steady_clock::now(); + + auto error = std::error_code(); + + m_offset = offset; + m_size = size; + m_buffer = buffer; + + radvisory args{}; + args.ra_offset = static_cast(offset); + args.ra_count = static_cast(size); + + auto result = fcntl(m_fd, F_RDADVISE, &args); + + if (result == -1) { + error = std::error_code(errno, std::system_category()); + } + + auto t2 = std::chrono::steady_clock::now(); + + m_submit_time += t2 - t1; + + return error; + } + + ReadResult WaitForResult() override + { + auto t1 = std::chrono::steady_clock::now(); + + auto result = pread(m_fd, m_buffer, m_size, m_offset); + + auto t2 = std::chrono::steady_clock::now(); + + m_wait_time += t2 - t1; + + if (result < 0) { + auto error = std::error_code(errno, std::system_category()); + return ReadResult{ 0, error }; + } + + auto bytes_read = static_cast(result); + + m_bytes_read += bytes_read; + + return ReadResult{ bytes_read, std::error_code() }; + } + + private: + int m_fd = -1; + off_t m_offset{}; + size_t m_size{}; + char* m_buffer{}; +}; + +#endif + +} // namespace sys + +using DataSource = std::variant; + +struct StreamReader : Reader { + StreamReader(std::istream* is) noexcept : m_stream(is) + { + assert(is); + if (!is->good()) { + m_error = std::make_error_code(std::io_errc::stream); + } + } + + std::error_code ReadBlock(size_t offset, size_t size, char* buffer) override + { + assert(buffer); + + ++m_num_requests; + + m_offset = offset; + m_size = size; + m_buffer = buffer; + + return std::error_code(); + } + + ReadResult WaitForResult() override + { + ReadResult result{}; + + auto t1 = std::chrono::steady_clock::now(); + + if (m_stream->read(m_buffer, m_size) || m_stream->eof()) { + result = { static_cast(m_stream->gcount()), std::error_code() }; + } else { + result = { size_t{}, std::make_error_code(std::io_errc::stream) }; + } + + auto t2 = std::chrono::steady_clock::now(); + + m_wait_time += t2 - t1; + + m_bytes_read += result.bytes_read; + + return result; + } + + private: + std::istream* m_stream{}; + size_t m_offset{}; + size_t m_size{}; + char* m_buffer{}; +}; + +inline std::unique_ptr CreateReader(sys::File* file) +{ + return std::make_unique(*file); +} +inline std::unique_ptr CreateReader(std::istream* is) +{ + return std::make_unique(is); +} +inline std::unique_ptr CreateReader(DataSource source) +{ + return std::visit([](auto arg) { return CreateReader(arg); }, source); +} + +inline std::string ToString(std::chrono::nanoseconds time, int width = 0) +{ + auto text = std::string(); + if (time.count() > 19'000'000'000) { + text = std::to_string(time.count() / 1'000'000'000); + text.append(" s"); + } else if (time.count() > 19'000'000) { + text = std::to_string(time.count() / 1'000'000); + text.append(" ms"); + } else if (time.count() > 19'000) { + text = std::to_string(time.count() / 1'000); + text.append(" us"); + } else { + text = std::to_string(time.count()); + text.append(" ns"); + } + auto n = width - static_cast(text.size()); + if (n > 0) { + text.insert(0, n, ' '); + } + return text; +} + +inline std::string ToString(size_t i, int width = 0) +{ + auto text = std::to_string(i); + auto n = width - static_cast(text.size()); + if (n > 0) { + text.insert(0, n, ' '); + } + return text; +} + +inline std::string RateToString(double bytes_per_second, int width = 0) +{ + const char* unit = " B/s"; + + if (bytes_per_second > 99_GiB) { + bytes_per_second /= 1_GiB; + unit = " GB/s"; + } else if (bytes_per_second > 99_MiB) { + bytes_per_second /= 1_MiB; + unit = " MB/s"; + } else if (bytes_per_second > 99_KiB) { + bytes_per_second /= 1_KiB; + unit = " KB/s"; + } + + auto text = std::to_string(static_cast(bytes_per_second)); + text.append(unit); + auto n = width - static_cast(text.size()); + if (n > 0) { + text.insert(0, n, ' '); + } + return text; +} + +inline std::string ComputeDebugStats(const std::vector& population) noexcept +{ + auto text = std::string(); + + auto min_time = std::chrono::nanoseconds{ std::chrono::nanoseconds::max() }; + auto max_time = std::chrono::nanoseconds{ std::chrono::nanoseconds::min() }; + auto min_index = size_t{}; + auto max_index = size_t{}; + auto avg_time = std::chrono::nanoseconds{}; + + for (size_t i = 0; i != population.size(); ++i) { + auto sample = population[i]; + if (sample < min_time) { + min_time = sample; + min_index = i; + } + if (sample > max_time) { + max_time = sample; + max_index = i; + } + avg_time += sample; + } + + avg_time /= population.size(); + + int64_t sq_sum{}; + + for (size_t i = 0; i != population.size(); ++i) { + auto sample = population[i]; + auto diff = (sample - avg_time).count(); + sq_sum += diff * diff; + } + + auto std_dev = std::chrono::nanoseconds(static_cast(std::sqrt(sq_sum / population.size()))); + + text.append("min: ").append(ToString(min_time, 10)); + text.append(" [#").append(std::to_string(min_index + 1)).append("]\n"); + text.append("max: ").append(ToString(max_time, 10)); + text.append(" [#").append(std::to_string(max_index + 1)).append("]\n"); + text.append("avg: ").append(ToString(avg_time, 10)).append("\n"); + text.append("std: ").append(ToString(std_dev, 10)).append("\n"); + + return text; +} + +inline std::string DumpDebug(const SharedContext& context) +{ + auto text = std::string(); + + auto population = std::vector(context.debug.io.num_requests.size()); + + auto num_bytes_read = size_t{}; + + for (size_t i = 0; i != context.debug.io.num_requests.size(); ++i) { + auto n = context.debug.io.num_requests[i]; + auto submit_ns = context.debug.io.submit_time[i]; + auto wait_ns = context.debug.io.wait_time[i]; + auto total_ns = submit_ns.count() + wait_ns.count(); + auto average_ns = total_ns / n; + auto thread = ToString(i + 1, 3); + auto requests = ToString(n, 5); + auto submit = ToString(std::chrono::nanoseconds(submit_ns), 9); + auto wait = ToString(std::chrono::nanoseconds(wait_ns), 9); + auto total = ToString(std::chrono::nanoseconds(total_ns), 9); + auto average = ToString(std::chrono::nanoseconds(average_ns), 9); + + population[i] = std::chrono::nanoseconds{ total_ns }; + + num_bytes_read += context.debug.io.num_bytes_read[i]; + + text.append(thread); + text.append(": blk").append(requests); + text.append(" read").append(submit); + text.append(" wait").append(wait); + text.append(" sum").append(total); + text.append(" avg").append(average); + text.push_back('\n'); + } + + text.push_back('\n'); + text.append(ComputeDebugStats(population)); + text.push_back('\n'); + + for (size_t i = 0; i != context.debug.io.num_requests.size(); ++i) { + auto parse_ns = context.debug.parse.time[i]; + auto io_ns = context.debug.io.submit_time[i] + context.debug.io.wait_time[i]; + auto io_percentage = static_cast(0.5f + 100.0f * io_ns.count() / parse_ns.count()); + auto thread = ToString(i + 1, 3); + auto parse = ToString(parse_ns, 9); + + population[i] = parse_ns; + + text.append(thread).append(": parse").append(parse); + text.append(" (").append(std::to_string(io_percentage)).append("% io)\n"); + } + + text.push_back('\n'); + text.append(ComputeDebugStats(population)); + text.push_back('\n'); + + auto parse_total = context.debug.parse.total_time; + auto merge_total = context.debug.merge.total_time; + auto total = parse_total + merge_total; + auto parse_percentage = static_cast(0.5f + 100.0f * parse_total.count() / total.count()); + auto merge_percentage = 100 - parse_percentage; + auto bytes_per_second = num_bytes_read / (parse_total.count() / 1000'000'000.0); + + text.append("Parse Time: "); + text.append(ToString(parse_total, 10)); + text.append(" (").append(std::to_string(parse_percentage)).append("%)\n"); + text.append("Merge Time: "); + text.append(ToString(merge_total, 10)); + text.append(" (").append(std::to_string(merge_percentage)).append("%)\n"); + text.append("Parse Rate: ").append(RateToString(bytes_per_second, 12)).append("\n"); + + return text; +} + +inline auto ParseXReals(std::string_view line, size_t max_count, float* out) +{ + size_t count = 0; + while (!line.empty() && count < max_count) { + TrimLeft(line); + auto [ptr, rc] = fast_float::from_chars(line.data(), line.data() + line.size(), *out); + if (rc != kSuccess) { + return std::make_pair(count, line); + } + auto num_parsed = static_cast(ptr - line.data()); + line.remove_prefix(num_parsed); + ++count; + ++out; + } + return std::make_pair(count, line); +} + +inline auto ParseXReals(std::string_view line, size_t max_count, Buffer* out) +{ + size_t count = 0; + out->ensure_enough_room_for(max_count); + while (!line.empty() && count < max_count) { + TrimLeft(line); + auto value = float(); + auto [ptr, rc] = fast_float::from_chars(line.data(), line.data() + line.size(), value); + if (rc != kSuccess) { + return std::make_pair(count, line); + } + auto num_parsed = static_cast(ptr - line.data()); + out->push_back(value); + line.remove_prefix(num_parsed); + ++count; + } + return std::make_pair(count, line); +} + +inline auto ParseReal(std::string_view line, float* out1) +{ + return ParseXReals(line, 1, out1); +} + +inline auto ParseReals(std::string_view line, float* out1, float* out2) +{ + float temp[2]; + auto result = ParseXReals(line, 2, temp); + *out1 = result.first > 0 ? temp[0] : *out1; + *out2 = result.first > 1 ? temp[1] : *out2; + return result; +} + +inline auto ParseReals(std::string_view line, float* out1, float* out2, float* out3) +{ + float temp[3]; + auto result = ParseXReals(line, 3, temp); + *out1 = result.first > 0 ? temp[0] : *out1; + *out2 = result.first > 1 ? temp[1] : *out2; + *out3 = result.first > 2 ? temp[2] : *out3; + return result; +} + +inline size_t ParseReals(std::string_view text, size_t max_count, float* out) +{ + auto count = size_t{}; + + TrimLeft(text); + + while (!text.empty() && count < max_count) { + auto [ptr, rc] = fast_float::from_chars(text.data(), text.data() + text.size(), *out); + if (rc != kSuccess) { + return 0; + } + auto num_parsed = static_cast(ptr - text.data()); + text.remove_prefix(num_parsed); + + TrimLeft(text); + + ++count; + ++out; + } + + TrimLeft(text); + + return text.empty() ? count : 0; +} + +inline size_t ParseReals(std::string_view text, size_t max_count, Float3* out) +{ + return ParseReals(text, max_count, out->data()); +} + +inline size_t ParseReals(std::string_view text, size_t max_count, Buffer* out) +{ + auto count = size_t{}; + + out->ensure_enough_room_for(max_count); + + TrimLeft(text); + + while (!text.empty() && count < max_count) { + auto value = float(); + auto [ptr, rc] = fast_float::from_chars(text.data(), text.data() + text.size(), value); + if (rc != kSuccess) { + return 0; + } + auto num_parsed = static_cast(ptr - text.data()); + out->push_back(value); + text.remove_prefix(num_parsed); + + TrimLeft(text); + + ++count; + } + + TrimLeft(text); + + return text.empty() ? count : 0; +} + +inline auto ParseFace( + std::string_view text, + size_t position_count, + size_t texcoord_count, + size_t normal_count, + size_t min_count, + size_t max_count, + OffsetFlags permitted_flags, + Buffer* indices, + Buffer* offset_flags) +{ + using std::make_pair; + + auto count = size_t{}; + auto value = 0; + + indices->ensure_enough_room_for(max_count); + offset_flags->ensure_enough_room_for(max_count); + + while (count <= max_count) { + TrimLeft(text); + + // exit if there is nothing left to process + if (text.empty()) { + break; + } + + // parse position index + { + auto [ptr, rc] = std::from_chars(text.data(), text.data() + text.size(), value); + if (rc != kSuccess) { + return make_pair(size_t{ 0 }, rapidobj_errc::ParseError); + } + auto num_parsed = static_cast(ptr - text.data()); + text.remove_prefix(num_parsed); + + offset_flags->push_back(static_cast(ApplyOffset::None)); + if (value > 0) { + --value; + } else if (value < 0) { + value += static_cast(position_count); + offset_flags->back() |= static_cast(ApplyOffset::Position); + } else { + return make_pair(size_t{ 0 }, rapidobj_errc::IndexOutOfBoundsError); + } + if (permitted_flags & ApplyOffset::Position) { + indices->push_back({ value, -1, -1 }); + ++count; + } else { + return make_pair(size_t{ 0 }, rapidobj_errc::ParseError); + } + } + + // exit if there is nothing left to process + if (text.empty()) { + break; + } + + // Parse UV + if (text.front() == '/') { + text.remove_prefix(1); + if (text.empty()) { + return make_pair(size_t{ 0 }, rapidobj_errc::ParseError); + } + if (text.front() != '/') { + // parse texcoord index + auto [ptr, rc] = std::from_chars(text.data(), text.data() + text.size(), value); + if (rc != kSuccess) { + return make_pair(size_t{ 0 }, rapidobj_errc::ParseError); + } + auto num_parsed = static_cast(ptr - text.data()); + text.remove_prefix(num_parsed); + + if (value > 0) { + --value; + } else if (value < 0) { + value += static_cast(texcoord_count); + offset_flags->back() |= static_cast(ApplyOffset::Texcoord); + } else { + return make_pair(size_t{ 0 }, rapidobj_errc::IndexOutOfBoundsError); + } + if (permitted_flags & ApplyOffset::Texcoord) { + indices->back().texcoord_index = value; + } else { + return make_pair(size_t{ 0 }, rapidobj_errc::ParseError); + } + } + } + + // exit if there is nothing left to process + if (text.empty()) { + break; + } + + // Parse Normal + if (text.front() == '/') { + text.remove_prefix(1); + if (text.empty()) { + return make_pair(size_t{ 0 }, rapidobj_errc::ParseError); + } + + // parse normal index + auto [ptr, rc] = std::from_chars(text.data(), text.data() + text.size(), value); + if (rc != kSuccess) { + return make_pair(size_t{ 0 }, rapidobj_errc::ParseError); + } + auto num_parsed = static_cast(ptr - text.data()); + text.remove_prefix(num_parsed); + + if (value > 0) { + --value; + } else if (value < 0) { + value += static_cast(normal_count); + offset_flags->back() |= static_cast(ApplyOffset::Normal); + } else { + return make_pair(size_t{ 0 }, rapidobj_errc::IndexOutOfBoundsError); + } + if (permitted_flags & ApplyOffset::Normal) { + indices->back().normal_index = value; + } else { + return make_pair(size_t{ 0 }, rapidobj_errc::ParseError); + } + } + } + + if (count < min_count) { + return make_pair(size_t{ 0 }, rapidobj_errc::TooFewIndicesError); + } + if (count > max_count) { + return make_pair(size_t{ 0 }, rapidobj_errc::TooManyIndicesError); + } + + return make_pair(count, rapidobj_errc::Success); +} + +inline auto ParseTextureOption(std::string_view line, TextureOption* texture_option) +{ + assert(texture_option); + + const auto fail = std::make_pair(std::string_view(), false); + + TrimRight(line); + + while (true) { + TrimLeft(line); + if (line.empty()) { + break; + } + if (line.front() != '-') { + return std::make_pair(line, true); + } else { + line.remove_prefix(1); + if (StartsWith(line, "blendu ") || StartsWith(line, "blendu\t")) { + line.remove_prefix(7); + TrimLeft(line); + if (StartsWith(line, "on")) { + line.remove_prefix(2); + texture_option->blendu = true; + } else if (StartsWith(line, "off")) { + line.remove_prefix(3); + texture_option->blendu = false; + } else { + return fail; + } + } else if (StartsWith(line, "blendv ") || StartsWith(line, "blendv\t")) { + line.remove_prefix(7); + TrimLeft(line); + if (StartsWith(line, "on")) { + line.remove_prefix(2); + texture_option->blendv = true; + } else if (StartsWith(line, "off")) { + line.remove_prefix(3); + texture_option->blendv = false; + } else { + return fail; + } + } else if (StartsWith(line, "boost ") || StartsWith(line, "boost\t")) { + line.remove_prefix(6); + auto [count, remainder] = ParseReal(line, &texture_option->sharpness); + if (count != 1) { + return fail; + } + line = remainder; + } else if (StartsWith(line, "mm ") || StartsWith(line, "mm\t")) { + line.remove_prefix(3); + auto [count, remainder] = ParseReals(line, &texture_option->brightness, &texture_option->contrast); + if (count != 2) { + return fail; + } + line = remainder; + } else if (StartsWith(line, "o ") || StartsWith(line, "o\t")) { + line.remove_prefix(2); + auto [count, remainder] = ParseReals( + line, + &texture_option->origin_offset[0], + &texture_option->origin_offset[1], + &texture_option->origin_offset[2]); + if (count < 1) { + return fail; + } + line = remainder; + } else if (StartsWith(line, "s ") || StartsWith(line, "s\t")) { + line.remove_prefix(2); + auto [count, remainder] = + ParseReals(line, &texture_option->scale[0], &texture_option->scale[1], &texture_option->scale[2]); + if (count < 1) { + return fail; + } + line = remainder; + } else if (StartsWith(line, "t ") || StartsWith(line, "t\t")) { + line.remove_prefix(2); + auto [count, remainder] = ParseReals( + line, + &texture_option->turbulence[0], + &texture_option->turbulence[1], + &texture_option->turbulence[2]); + if (count < 1) { + return fail; + } + line = remainder; + } else if (StartsWith(line, "texres ") || StartsWith(line, "texres\t")) { + line.remove_prefix(7); + TrimLeft(line); + auto [ptr, rc] = + std::from_chars(line.data(), line.data() + line.size(), texture_option->texture_resolution); + if (rc != kSuccess) { + return fail; + } + auto num_parsed = static_cast(ptr - line.data()); + line.remove_prefix(num_parsed); + } else if (StartsWith(line, "clamp ") || StartsWith(line, "clamp\t")) { + line.remove_prefix(6); + TrimLeft(line); + if (StartsWith(line, "on")) { + line.remove_prefix(2); + texture_option->clamp = true; + } else if (StartsWith(line, "off")) { + line.remove_prefix(3); + texture_option->clamp = false; + } else { + return fail; + } + } else if (StartsWith(line, "bm ") || StartsWith(line, "bm\t")) { + line.remove_prefix(3); + auto [count, remainder] = ParseReal(line, &texture_option->bump_multiplier); + if (count < 1) { + return fail; + } + line = remainder; + } else if (StartsWith(line, "imfchan ") || StartsWith(line, "imfchan\t")) { + line.remove_prefix(8); + char c = line.empty() ? '?' : line.front(); + if (c == 'r' || c == 'g' || c == 'b' || c == 'm' || c == 'l' || c == 'z') { + texture_option->imfchan = c; + line.remove_prefix(1); + } else { + return fail; + } + } else if (StartsWith(line, "type ") || StartsWith(line, "type\t")) { + line.remove_prefix(5); + if (StartsWith(line, "sphere")) { + line.remove_prefix(6); + texture_option->type = TextureType::Sphere; + } else if (StartsWith(line, "cube_top")) { + line.remove_prefix(8); + texture_option->type = TextureType::CubeTop; + } else if (StartsWith(line, "cube_bottom")) { + line.remove_prefix(11); + texture_option->type = TextureType::CubeBottom; + } else if (StartsWith(line, "cube_front")) { + line.remove_prefix(10); + texture_option->type = TextureType::CubeFront; + } else if (StartsWith(line, "cube_back")) { + line.remove_prefix(9); + texture_option->type = TextureType::CubeBack; + } else if (StartsWith(line, "cube_left")) { + line.remove_prefix(9); + texture_option->type = TextureType::CubeLeft; + } else if (StartsWith(line, "cube_right")) { + line.remove_prefix(10); + texture_option->type = TextureType::CubeRight; + } else { + return fail; + } + } else { + return fail; + } + if (!line.empty() && line.front() != ' ' && line.front() != '\t') { + return fail; + } + } + } + + return std::make_pair(line, true); +} + +inline ParseMaterialsResult ParseMaterials(std::string_view text) +{ + auto material_map = MaterialMap{}; + auto material_id = 0; + auto materials = Materials{}; + auto material = Material{}; + + material.bump_texopt.imfchan = 'l'; + + auto line_num = size_t{}; + + bool has_dissolve = false; + + while (text.empty() == false) { + auto line_parsed = false; + auto line = std::string_view(); + { + auto ptr = static_cast(memchr(text.data(), '\n', text.size())); + auto len = static_cast(ptr - text.data()); + bool eol = ptr; + line = eol ? text.substr(0, len) : text; + text.remove_prefix(line.size() + eol); + if (EndsWith(line, '\r')) { + line.remove_suffix(1); + } + ++line_num; + } + auto line_clone = line; + Trim(line); + if (line.empty()) { + continue; + } + switch (line.front()) { + case 'n': { + if (StartsWith(line, "newmtl ")) { + line.remove_prefix(7); + Trim(line); + if (!material.name.empty()) { + materials.push_back(std::move(material)); + material = {}; + } + material.name = line; + material.bump_texopt.imfchan = 'l'; + material_map.try_emplace(std::string(line), material_id++); + has_dissolve = false; + line_parsed = true; + } else if (StartsWith(line, "norm ") || StartsWith(line, "norm\t")) { + line.remove_prefix(5); + auto [remainder, success] = ParseTextureOption(line, &material.normal_texopt); + material.normal_texname = remainder; + line_parsed = success; + } + break; + } + case 'K': { + if (StartsWith(line, "Ka ") || StartsWith(line, "Ka\t")) { + line.remove_prefix(3); + line_parsed = 3 == ParseReals(line, 3, &material.ambient); + } else if (StartsWith(line, "Kd ") || StartsWith(line, "Kd\t")) { + line.remove_prefix(3); + line_parsed = 3 == ParseReals(line, 3, &material.diffuse); + } else if (StartsWith(line, "Ks ") || StartsWith(line, "Ks\t")) { + line.remove_prefix(3); + line_parsed = 3 == ParseReals(line, 3, &material.specular); + } else if (StartsWith(line, "Kt ") || StartsWith(line, "Kt\t")) { + line.remove_prefix(3); + line_parsed = 3 == ParseReals(line, 3, &material.transmittance); + } else if (StartsWith(line, "Ke ") || StartsWith(line, "Ke\t")) { + line.remove_prefix(3); + line_parsed = 3 == ParseReals(line, 3, &material.emission); + } else if (StartsWith(line, "Km ") || StartsWith(line, "Km\t")) { + // ignore + line_parsed = true; + } + break; + } + case 'T': { + if (StartsWith(line, "Tf ") || StartsWith(line, "Tf\t")) { + line.remove_prefix(3); + line_parsed = 3 == ParseReals(line, 3, &material.transmittance); + } else if (StartsWith(line, "Tr ") || StartsWith(line, "Tr\t")) { + line.remove_prefix(3); + auto transparency = 0.0f; + line_parsed = 1 == ParseReals(line, 1, &transparency); + if (!has_dissolve) { + material.dissolve = 1.0f - transparency; + } + } + break; + } + case 'd': { + if (StartsWith(line, "disp ") || StartsWith(line, "disp\t")) { + line.remove_prefix(5); + auto [remainder, success] = ParseTextureOption(line, &material.displacement_texopt); + material.displacement_texname = remainder; + line_parsed = success; + } else if (StartsWith(line, "d ") || StartsWith(line, "d\t")) { + line.remove_prefix(2); + line_parsed = 1 == ParseReals(line, 1, &material.dissolve); + has_dissolve = true; + } + break; + } + case 'a': { + if (StartsWith(line, "aniso ") || StartsWith(line, "aniso\t")) { + line.remove_prefix(6); + line_parsed = 1 == ParseReals(line, 1, &material.anisotropy); + } else if (StartsWith(line, "anisor ") || StartsWith(line, "anisor\t")) { + line.remove_prefix(7); + line_parsed = 1 == ParseReals(line, 1, &material.anisotropy_rotation); + } + break; + } + case 'i': { + if (StartsWith(line, "illum ") || StartsWith(line, "illum\t")) { + line.remove_prefix(6); + TrimLeft(line); + auto [ptr, rc] = std::from_chars(line.data(), line.data() + line.size(), material.illum); + if (rc == kSuccess) { + auto num_parsed = static_cast(ptr - line.data()); + line.remove_prefix(num_parsed); + Trim(line); + line_parsed = line.empty(); + } + } + break; + } + case 'b': { + if (StartsWith(line, "bump ") || StartsWith(line, "bump\t")) { + line.remove_prefix(5); + auto [remainder, success] = ParseTextureOption(line, &material.bump_texopt); + material.bump_texname = remainder; + line_parsed = success; + } + break; + } + case 'r': { + if (StartsWith(line, "refl ") || StartsWith(line, "refl\t")) { + line.remove_prefix(5); + auto [remainder, success] = ParseTextureOption(line, &material.reflection_texopt); + material.reflection_texname = remainder; + line_parsed = success; + } + break; + } + case 'm': { + if (StartsWith(line, "map_Ka ") || StartsWith(line, "map_Ka\t")) { + line.remove_prefix(7); + auto [remainder, success] = ParseTextureOption(line, &material.ambient_texopt); + material.ambient_texname = remainder; + line_parsed = success; + } else if (StartsWith(line, "map_Kd ") || StartsWith(line, "map_Kd\t")) { + line.remove_prefix(7); + auto [remainder, success] = ParseTextureOption(line, &material.diffuse_texopt); + material.diffuse_texname = remainder; + line_parsed = success; + } else if (StartsWith(line, "map_Ks ") || StartsWith(line, "map_Ks\t")) { + line.remove_prefix(7); + auto [remainder, success] = ParseTextureOption(line, &material.specular_texopt); + material.specular_texname = remainder; + line_parsed = success; + } else if (StartsWith(line, "map_Ns ") || StartsWith(line, "map_Ns\t")) { + line.remove_prefix(7); + auto [remainder, success] = ParseTextureOption(line, &material.specular_highlight_texopt); + material.specular_highlight_texname = remainder; + line_parsed = success; + } else if ( + StartsWith(line, "map_bump ") || StartsWith(line, "map_bump\t") || StartsWith(line, "map_Bump ") || + StartsWith(line, "map_Bump\t")) { + line.remove_prefix(9); + auto [remainder, success] = ParseTextureOption(line, &material.bump_texopt); + material.bump_texname = remainder; + line_parsed = success; + } else if (StartsWith(line, "map_d ") || StartsWith(line, "map_d\t")) { + line.remove_prefix(6); + auto [remainder, success] = ParseTextureOption(line, &material.alpha_texopt); + material.alpha_texname = remainder; + line_parsed = success; + } else if (StartsWith(line, "map_Pr ") || StartsWith(line, "map_Pr\t")) { + line.remove_prefix(7); + auto [remainder, success] = ParseTextureOption(line, &material.roughness_texopt); + material.roughness_texname = remainder; + line_parsed = success; + } else if (StartsWith(line, "map_Pm ") || StartsWith(line, "map_Pm\t")) { + line.remove_prefix(7); + auto [remainder, success] = ParseTextureOption(line, &material.metallic_texopt); + material.metallic_texname = remainder; + line_parsed = success; + } else if (StartsWith(line, "map_Ps ") || StartsWith(line, "map_Ps\t")) { + line.remove_prefix(7); + auto [remainder, success] = ParseTextureOption(line, &material.sheen_texopt); + material.sheen_texname = remainder; + line_parsed = success; + } else if (StartsWith(line, "map_Ke ") || StartsWith(line, "map_Ke\t")) { + line.remove_prefix(7); + auto [remainder, success] = ParseTextureOption(line, &material.emissive_texopt); + material.emissive_texname = remainder; + line_parsed = success; + } + break; + } + case 'N': { + if (StartsWith(line, "Ns ") || StartsWith(line, "Ns\t")) { + line.remove_prefix(3); + line_parsed = 1 == ParseReals(line, 1, &material.shininess); + } else if (StartsWith(line, "Ni ") || StartsWith(line, "Ni\t")) { + line.remove_prefix(3); + line_parsed = 1 == ParseReals(line, 1, &material.ior); + } + break; + } + case 'P': { + if (StartsWith(line, "Pr ") || StartsWith(line, "Pr\t")) { + line.remove_prefix(3); + line_parsed = 1 == ParseReals(line, 1, &material.roughness); + } else if (StartsWith(line, "Pm ") || StartsWith(line, "Pm\t")) { + line.remove_prefix(3); + line_parsed = 1 == ParseReals(line, 1, &material.metallic); + } else if (StartsWith(line, "Ps ") || StartsWith(line, "Ps\t")) { + line.remove_prefix(3); + line_parsed = 1 == ParseReals(line, 1, &material.sheen); + } else if (StartsWith(line, "Pc ") || StartsWith(line, "Pc\t")) { + line.remove_prefix(3); + line_parsed = 1 == ParseReals(line, 1, &material.clearcoat_thickness); + } else if (StartsWith(line, "Pcr ") || StartsWith(line, "Pcr\t")) { + line.remove_prefix(4); + line_parsed = 1 == ParseReals(line, 1, &material.clearcoat_roughness); + } + break; + } + case '#': { + line_parsed = true; + break; + } + } // end switch + if (!line_parsed && line_num > 1) { + return { MaterialMap{}, + Materials{}, + Error{ make_error_code(rapidobj_errc::MaterialParseError), std::string(line_clone), line_num } }; + } + } // end loop + + if (!material.name.empty()) { + materials.push_back(std::move(material)); + } + + return { std::move(material_map), std::move(materials), Error{} }; +} + +inline auto FindBestPath(SharedContext* context) +{ + const auto& basepath = context->material.basepath; + const auto& paths = std::get>(context->material.library->Value()); + + for (const auto& path : paths) { + auto bestpath = path.is_absolute() ? path : (basepath / path); + if (std::filesystem::is_directory(bestpath)) { + bestpath /= context->material.library_name; + } + if (std::filesystem::exists(bestpath) && std::filesystem::is_regular_file(bestpath)) { + return bestpath; + } + } + + return std::filesystem::path(); +} + +inline auto ParseMaterialLibrary(SharedContext* context) +{ + if (std::holds_alternative(context->material.library->Value())) { + return ParseMaterials(std::get(context->material.library->Value())); + } + + auto filepath = FindBestPath(context); + + if (filepath.empty()) { + return ParseMaterialsResult{ {}, {}, Error{ make_error_code(rapidobj_errc::MaterialFileError) } }; + } + + auto file = std::ifstream(filepath, std::ios::in | std::ios::binary | std::ios::ate); + + auto filesize = file.tellg(); + if (filesize <= 0) { + return ParseMaterialsResult{ {}, {}, Error{ std::make_error_code(std::io_errc::stream) } }; + } + file.seekg(0, std::ios::beg); + + auto filedata = Buffer(static_cast(filesize)); + if (file.is_open()) { + file.read(filedata.data(), filesize); + } else { + return ParseMaterialsResult{ {}, {}, Error{ std::make_error_code(std::io_errc::stream) } }; + } + file.close(); + + return ParseMaterials(std::string_view(filedata.data(), static_cast(filesize))); +} + +inline void DispatchMergeTasks(const MergeTasks& tasks, std::shared_ptr context) +{ + while (true) { + auto fetched_index = std::atomic_fetch_add(&context->merging.task_index, size_t(1)); + + if (fetched_index >= tasks.size()) { + break; + } + + const auto& merge_task = tasks[fetched_index]; + + if (auto rc = std::visit([](const auto& task) { return task.Execute(); }, merge_task); + rc != rapidobj_errc::Success) { + std::lock_guard lock(context->merging.mutex); + if (context->merging.error == rapidobj_errc::Success) { + context->merging.error = rc; + } + break; + } + } + + if (1 == std::atomic_fetch_sub(&context->merging.thread_count, size_t(1))) { + context->merging.completed.set_value(); + } +} + +inline void MergeSequential(MergeTasks* tasks, std::shared_ptr context) +{ + DispatchMergeTasks(std::vector(tasks->begin(), tasks->end()), context); +} + +inline void MergeParallel(MergeTasks* merge_tasks, std::shared_ptr context) +{ + auto tasks = MergeTasks(); + tasks.reserve(merge_tasks->size()); + + for (auto& merge_task : *merge_tasks) { + auto cost = std::visit([](const auto& task) { return task.Cost(); }, merge_task); + + if (cost >= kMergeSubdivideCost) { + auto divisor = std::max(size_t(2), cost / kMergeSubdivideCost); + auto subtasks = std::visit([divisor](const auto& task) { return task.Subdivide(divisor); }, merge_task); + for (auto& subtask : subtasks) { + tasks.push_back(std::move(subtask)); + } + } else { + tasks.push_back(std::move(merge_task)); + } + } + + auto threads = std::vector{}; + threads.reserve(context->thread.concurrency); + + context->merging.thread_count = context->thread.concurrency; + + for (size_t i = 0; i != context->thread.concurrency; ++i) { + threads.emplace_back(DispatchMergeTasks, tasks, context); + threads.back().detach(); + } + + // wait for merging to finish + context->merging.completed.get_future().wait(); +} + +// Merge function helper structs +struct ListInfo final { + size_t face_buffers_size{}; + size_t shape_records_size{}; + size_t material_offsets_size{}; + size_t smoothing_offsets_size{}; + + ListInfo& operator+=(const ListInfo& rhs) noexcept + { + face_buffers_size += rhs.face_buffers_size; + shape_records_size += rhs.shape_records_size; + material_offsets_size += rhs.material_offsets_size; + smoothing_offsets_size += rhs.smoothing_offsets_size; + return *this; + } +}; + +struct ShapeInfo final { + struct Mesh final { + size_t index_array_size{}; + size_t faces_array_size{}; + }; + struct Line final { + size_t index_array_size{}; + size_t segment_array_size{}; + }; + struct Point final { + size_t index_array_size{}; + }; + Mesh mesh; + Line line; + Point point; +}; + +struct Offset final { + size_t position{}; + size_t texcoord{}; + size_t normal{}; + size_t face{}; + + Offset& operator+=(const Offset& rhs) noexcept + { + position += rhs.position; + texcoord += rhs.texcoord; + normal += rhs.normal; + face += rhs.face; + return *this; + } +}; + +inline Result Merge(const std::vector& chunks, std::shared_ptr context) +{ + // compute overall sizes of lists + auto list_info = ListInfo{}; + for (const Chunk& chunk : chunks) { + list_info += { chunk.mesh.faces.buffer.size(), + chunk.shapes.list.size(), + chunk.materials.list.size(), + chunk.smoothing.list.size() }; + } + + // compute offsets for each chunk and total attribute count + auto offsets = std::vector(); + auto count = AttributeInfo{}; + { + offsets.reserve(chunks.size()); + auto running = Offset{}; + for (const Chunk& chunk : chunks) { + offsets.push_back(running); + running += { chunk.positions.count, chunk.texcoords.count, chunk.normals.count, chunk.mesh.faces.count }; + } + count = { running.position, running.texcoord, running.normal }; + context->stats = { running.position, running.texcoord, running.normal }; + } + + // coalesce per-chunk shape records into a single shape records list + auto shape_records = std::vector(); + { + shape_records.reserve(list_info.shape_records_size + 2); + shape_records.push_back({}); + for (size_t i = 0; i != chunks.size(); ++i) { + for (const ShapeRecord& record : chunks[i].shapes.list) { + shape_records.push_back(record); + shape_records.back().chunk_index = i; + } + } + shape_records.push_back({}); + shape_records.back().mesh.index_buffer_start = chunks.back().mesh.indices.buffer.size(); + shape_records.back().mesh.face_buffer_start = chunks.back().mesh.faces.buffer.size(); + shape_records.back().lines.index_buffer_start = chunks.back().lines.indices.buffer.size(); + shape_records.back().lines.segment_buffer_start = chunks.back().lines.segments.buffer.size(); + shape_records.back().points.index_buffer_start = chunks.back().points.indices.buffer.size(); + shape_records.back().chunk_index = chunks.size() - 1; + } + + // prepare smoothing-source list from per-chunk smoothing-record lists + auto smoothing_src = std::vector>(); + { + smoothing_src.reserve(list_info.smoothing_offsets_size + 2); + smoothing_src.push_back({ 0, 0 }); + for (size_t i = 0; i != chunks.size(); ++i) { + for (const SmoothingRecord& record : chunks[i].smoothing.list) { + if (!smoothing_src.empty()) { + if (record.face_buffer_start + offsets[i].face == smoothing_src.back().offset) { + smoothing_src.pop_back(); + } + } + smoothing_src.push_back({ record.group_id, record.face_buffer_start + offsets[i].face }); + } + } + smoothing_src.push_back({ 0, list_info.face_buffers_size }); + } + + auto parsed_materials = ParseMaterialsResult{}; + + if (context->material.parse_result.valid()) { + parsed_materials = context->material.parse_result.get(); + if (parsed_materials.error.code) { + if (context->material.library->Policy() == Load::Optional) { + parsed_materials = {}; + int id = 0; + for (const Chunk& chunk : chunks) { + for (const MaterialRecord& record : chunk.materials.list) { + auto [it, emplaced] = parsed_materials.material_map.try_emplace(std::string(record.name), id); + id += emplaced ? 1 : 0; + } + } + } else { + return Result{ Attributes{}, Shapes{}, Materials{}, { parsed_materials.error.code } }; + } + } + } + + // prepare material-source list from per-chunk material-record lists + auto material_src = std::vector>(); + if (context->material.library) { + material_src.reserve(list_info.material_offsets_size + 2); + material_src.push_back({ -1, 0 }); + for (size_t i = 0; i != chunks.size(); ++i) { + for (const MaterialRecord& record : chunks[i].materials.list) { + if (!material_src.empty()) { + if (record.face_buffer_start + offsets[i].face == material_src.back().offset) { + material_src.pop_back(); + } + } + auto it = parsed_materials.material_map.find(record.name); + if (it != parsed_materials.material_map.end()) { + material_src.push_back({ it->second, record.face_buffer_start + offsets[i].face }); + } else { + auto line_num = record.line_num; + for (size_t j = 0; j != i; ++j) { + line_num += chunks[j].text.line_count; + } + auto error = Error{ rapidobj_errc::MaterialNotFoundError, record.line, line_num }; + return Result{ Attributes{}, Shapes{}, Materials{}, std::move(error) }; + } + } + } + material_src.push_back({ -1, list_info.face_buffers_size }); + } + + auto shapes = Shapes(); + auto tasks = MergeTasks(); + + shapes.reserve(shape_records.size()); + + // this big loop allocates shapes and computes tasks that construct them + for (size_t i = 0; i != shape_records.size() - 1; ++i) { + auto& shape = shape_records[i]; + auto& next = shape_records[i + 1]; + + // compute shape info + auto shape_info = ShapeInfo{}; + + for (size_t j = shape.chunk_index; j <= next.chunk_index; ++j) { + auto mesh_index_buffer_size = chunks[j].mesh.indices.buffer.size(); + auto mesh_faces_buffer_size = chunks[j].mesh.faces.buffer.size(); + auto lines_index_buffer_size = chunks[j].lines.indices.buffer.size(); + auto lines_segment_buffer_size = chunks[j].lines.segments.buffer.size(); + auto points_index_buffer_size = chunks[j].points.indices.buffer.size(); + if (shape.chunk_index == next.chunk_index) { + shape_info.mesh.index_array_size = next.mesh.index_buffer_start - shape.mesh.index_buffer_start; + shape_info.mesh.faces_array_size = next.mesh.face_buffer_start - shape.mesh.face_buffer_start; + shape_info.line.index_array_size = next.lines.index_buffer_start - shape.lines.index_buffer_start; + shape_info.line.segment_array_size = next.lines.segment_buffer_start - shape.lines.segment_buffer_start; + shape_info.point.index_array_size = next.points.index_buffer_start - shape.points.index_buffer_start; + } else if (j == shape.chunk_index) { + shape_info.mesh.index_array_size = mesh_index_buffer_size - shape.mesh.index_buffer_start; + shape_info.mesh.faces_array_size = mesh_faces_buffer_size - shape.mesh.face_buffer_start; + shape_info.line.index_array_size = lines_index_buffer_size - shape.lines.index_buffer_start; + shape_info.line.segment_array_size = lines_segment_buffer_size - shape.lines.segment_buffer_start; + shape_info.point.index_array_size = points_index_buffer_size - shape.points.index_buffer_start; + } else if (j == next.chunk_index) { + shape_info.mesh.index_array_size += next.mesh.index_buffer_start; + shape_info.mesh.faces_array_size += next.mesh.face_buffer_start; + shape_info.line.index_array_size += next.lines.index_buffer_start; + shape_info.line.segment_array_size += next.lines.segment_buffer_start; + shape_info.point.index_array_size += next.points.index_buffer_start; + } else { + shape_info.mesh.index_array_size += mesh_index_buffer_size; + shape_info.mesh.faces_array_size += mesh_faces_buffer_size; + shape_info.line.index_array_size += lines_index_buffer_size; + shape_info.line.segment_array_size += lines_segment_buffer_size; + shape_info.point.index_array_size += points_index_buffer_size; + } + } + + // skip empty shape + if (!shape_info.mesh.index_array_size && !shape_info.line.index_array_size && + !shape_info.point.index_array_size) { + continue; + } + + auto num_indices = shape_info.mesh.index_array_size; + auto num_faces = shape_info.mesh.faces_array_size; + auto num_material_ids = context->material.library ? num_faces : 0; + auto num_smoothing_ids = num_faces; + + // allocate Shape + shapes.push_back(Shape{ + std::move(shape.name), + Mesh{ Array(num_indices), + Array(num_faces), + Array(num_material_ids), + Array(num_smoothing_ids) }, + Lines{ Array(shape_info.line.index_array_size), Array(shape_info.line.segment_array_size) }, + Points{ Array(shape_info.point.index_array_size) } }); + + // compute tasks to construct shape mesh + if (shape_info.mesh.index_array_size) { + // compute mesh mem copies + { + auto index_dst = shapes.back().mesh.indices.begin(); + auto nface_dst = shapes.back().mesh.num_face_vertices.begin(); + + for (size_t j = shape.chunk_index; j <= next.chunk_index; ++j) { + auto index_src = chunks[j].mesh.indices.buffer.data(); + auto index_flags = chunks[j].mesh.indices.flags.data(); + auto index_size = chunks[j].mesh.indices.buffer.size(); + auto nface_src = chunks[j].mesh.faces.buffer.data(); + auto nface_size = chunks[j].mesh.faces.buffer.size(); + if (shape.chunk_index == next.chunk_index) { + index_src = index_src + shape.mesh.index_buffer_start; + index_flags = index_flags + shape.mesh.index_buffer_start; + index_size = next.mesh.index_buffer_start - shape.mesh.index_buffer_start; + nface_src = nface_src + shape.mesh.face_buffer_start; + nface_size = next.mesh.face_buffer_start - shape.mesh.face_buffer_start; + } else if (j == shape.chunk_index) { + index_src = index_src + shape.mesh.index_buffer_start; + index_flags = index_flags + shape.mesh.index_buffer_start; + index_size = index_size - shape.mesh.index_buffer_start; + nface_src = nface_src + shape.mesh.face_buffer_start; + nface_size = nface_size - shape.mesh.face_buffer_start; + } else if (j == next.chunk_index) { + index_size = next.mesh.index_buffer_start; + nface_size = next.mesh.face_buffer_start; + } + if (index_size) { + auto offset = AttributeInfo{ offsets[j].position, offsets[j].texcoord, offsets[j].normal }; + tasks.push_back(CopyIndices(index_dst, index_src, index_flags, index_size, offset, count)); + index_dst += index_size; + } + if (nface_size) { + tasks.push_back(CopyBytes(nface_dst, nface_src, nface_size)); + nface_dst += nface_size; + } + } + } + // compute material id fills + if (!material_src.empty()) { + auto dst = shapes.back().mesh.material_ids.begin(); + auto size = shape_info.mesh.faces_array_size; + auto start = shape.mesh.face_buffer_start + offsets[shape.chunk_index].face; + tasks.push_back(FillMaterialIds(dst, material_src, size, start)); + } + // compute smoothing group id fills + { + auto dst = shapes.back().mesh.smoothing_group_ids.begin(); + auto size = shape_info.mesh.faces_array_size; + auto start = shape.mesh.face_buffer_start + offsets[shape.chunk_index].face; + tasks.push_back(FillSmoothingGroupIds(dst, smoothing_src, size, start)); + } + } + + // compute tasks to construct shape lines + if (shape_info.line.index_array_size) { + auto index_dst = shapes.back().lines.indices.begin(); + auto nline_dst = shapes.back().lines.num_line_vertices.begin(); + + for (size_t j = shape.chunk_index; j <= next.chunk_index; ++j) { + auto index_src = chunks[j].lines.indices.buffer.data(); + auto index_flags = chunks[j].lines.indices.flags.data(); + auto index_size = chunks[j].lines.indices.buffer.size(); + auto nline_src = chunks[j].lines.segments.buffer.data(); + auto nline_size = chunks[j].lines.segments.buffer.size(); + if (shape.chunk_index == next.chunk_index) { + index_src = index_src + shape.lines.index_buffer_start; + index_flags = index_flags + shape.lines.index_buffer_start; + index_size = next.lines.index_buffer_start - shape.lines.index_buffer_start; + nline_src = nline_src + shape.lines.segment_buffer_start; + nline_size = next.lines.segment_buffer_start - shape.lines.segment_buffer_start; + } else if (j == shape.chunk_index) { + index_src = index_src + shape.lines.index_buffer_start; + index_flags = index_flags + shape.lines.index_buffer_start; + index_size = index_size - shape.lines.index_buffer_start; + nline_src = nline_src + shape.lines.segment_buffer_start; + nline_size = nline_size - shape.lines.segment_buffer_start; + } else if (j == next.chunk_index) { + index_size = next.lines.index_buffer_start; + nline_size = next.lines.segment_buffer_start; + } + if (index_size) { + auto offset = AttributeInfo{ offsets[j].position, offsets[j].texcoord, offsets[j].normal }; + tasks.push_back(CopyIndices(index_dst, index_src, index_flags, index_size, offset, count)); + index_dst += index_size; + } + if (nline_size) { + tasks.push_back(CopyInts(nline_dst, nline_src, nline_size)); + nline_dst += nline_size; + } + } + } + + // compute tasks to construct shape points + if (shape_info.point.index_array_size) { + auto index_dst = shapes.back().points.indices.begin(); + + for (size_t j = shape.chunk_index; j <= next.chunk_index; ++j) { + auto index_src = chunks[j].points.indices.buffer.data(); + auto index_flags = chunks[j].points.indices.flags.data(); + auto index_size = chunks[j].points.indices.buffer.size(); + if (shape.chunk_index == next.chunk_index) { + index_src = index_src + shape.points.index_buffer_start; + index_flags = index_flags + shape.points.index_buffer_start; + index_size = next.points.index_buffer_start - shape.points.index_buffer_start; + } else if (j == shape.chunk_index) { + index_src = index_src + shape.points.index_buffer_start; + index_flags = index_flags + shape.points.index_buffer_start; + index_size = index_size - shape.points.index_buffer_start; + } else if (j == next.chunk_index) { + index_size = next.points.index_buffer_start; + } + if (index_size) { + auto offset = AttributeInfo{ offsets[j].position, offsets[j].texcoord, offsets[j].normal }; + tasks.push_back(CopyIndices(index_dst, index_src, index_flags, index_size, offset, count)); + index_dst += index_size; + } + } + } + } + + // compute overall attribute array sizes + auto attribute_size = AttributeInfo{}; + bool has_vertex_colors = false; + + for (const Chunk& chunk : chunks) { + attribute_size += { chunk.positions.buffer.size(), chunk.texcoords.buffer.size(), chunk.normals.buffer.size() }; + has_vertex_colors |= chunk.colors.count ? true : false; + } + + auto attribute_size_color = has_vertex_colors ? attribute_size.position : size_t{}; + + // allocate attribute arrays + auto attributes = Attributes{ { attribute_size.position }, + { attribute_size.texcoord }, + { attribute_size.normal }, + { attribute_size_color } }; + + // compute tasks to construct attribute arrays + auto positions_destination = attributes.positions.data(); + auto texcoords_destination = attributes.texcoords.data(); + auto normals_destination = attributes.normals.data(); + auto colors_destination = attributes.colors.data(); + + for (const Chunk& chunk : chunks) { + if (chunk.positions.buffer.size()) { + auto dst = positions_destination; + auto src = chunk.positions.buffer.data(); + auto size = chunk.positions.buffer.size(); + tasks.push_back(CopyFloats(dst, src, size)); + positions_destination += size; + } + if (chunk.texcoords.buffer.size()) { + auto dst = texcoords_destination; + auto src = chunk.texcoords.buffer.data(); + auto size = chunk.texcoords.buffer.size(); + tasks.push_back(CopyFloats(dst, src, size)); + texcoords_destination += size; + } + if (chunk.normals.buffer.size()) { + auto dst = normals_destination; + auto src = chunk.normals.buffer.data(); + auto size = chunk.normals.buffer.size(); + tasks.push_back(CopyFloats(dst, src, size)); + normals_destination += size; + } + if (chunk.colors.buffer.size()) { + auto dst = colors_destination; + auto src = chunk.colors.buffer.data(); + auto size = chunk.colors.buffer.size(); + tasks.push_back(CopyFloats(dst, src, size)); + colors_destination += size; + } else if (has_vertex_colors) { + auto dst = colors_destination; + auto val = 1.0f; + auto size = chunk.positions.buffer.size(); + tasks.push_back(FillFloats(dst, val, size)); + colors_destination += size; + } + } + + // perform merge + if (context->thread.concurrency > 1) { + MergeParallel(&tasks, context); + } else { + MergeSequential(&tasks, context); + } + + if (context->merging.error != rapidobj_errc::Success) { + auto error = make_error_code(context->merging.error); + return Result{ Attributes{}, Shapes{}, Materials{}, Error{ error } }; + } + + return Result{ std::move(attributes), std::move(shapes), std::move(parsed_materials.materials), Error{} }; +} + +inline auto ParsePosition(std::string_view line, Chunk* chunk) +{ + auto [count, remainder] = ParseXReals(line, 3, &chunk->positions.buffer); + if (count < 3) { + return rapidobj_errc::ParseError; + } + ++chunk->positions.count; + auto [count2, remainder2] = ParseXReals(remainder, 3, &chunk->colors.buffer); + if (count2 == 0) { + if (chunk->colors.buffer.size()) { + chunk->colors.buffer.ensure_enough_room_for(3); + chunk->colors.buffer.fill_n(3, 1.0f); + ++chunk->colors.count; + } + } else if (count2 == 3) { + if ((chunk->colors.buffer.size() == 3) && (chunk->positions.buffer.size() > 3)) { + size_t n = chunk->positions.buffer.size(); + float b = chunk->colors.buffer.pop_back(); + float g = chunk->colors.buffer.pop_back(); + float r = chunk->colors.buffer.pop_back(); + chunk->colors.buffer.ensure_enough_room_for(n); + chunk->colors.buffer.fill_n(n - 3, 1.0f); + chunk->colors.buffer.push_back(r); + chunk->colors.buffer.push_back(g); + chunk->colors.buffer.push_back(b); + } + ++chunk->colors.count; + } else { + return rapidobj_errc::ParseError; + } + TrimLeft(remainder2); + if (!remainder2.empty()) { + return rapidobj_errc::ParseError; + } + return rapidobj_errc::Success; +} + +inline rapidobj_errc ProcessLine(std::string_view line, Chunk* chunk, SharedContext* context) +{ + const auto text = line; + + TrimLeft(line); + + // skip empty lines + if (line.empty()) { + return rapidobj_errc::Success; + } + + // process token + switch (line.front()) { + case 'v': { + if (StartsWith(line, "v ") || StartsWith(line, "v\t")) { + line.remove_prefix(2); + if (auto rc = ParsePosition(line, chunk); rc != rapidobj_errc::Success) { + return rc; + } + } else if (StartsWith(line, "vt ") || StartsWith(line, "vt\t")) { + line.remove_prefix(3); + auto count = ParseReals(line, 3, &chunk->texcoords.buffer); + if (count < 2) { + return rapidobj_errc::ParseError; + } else if (count == 3) { + chunk->texcoords.buffer.pop_back(); + } + ++chunk->texcoords.count; + } else if (StartsWith(line, "vn ") || StartsWith(line, "vn\t")) { + line.remove_prefix(3); + auto count = ParseReals(line, 3, &chunk->normals.buffer); + if (count < 3) { + return rapidobj_errc::ParseError; + } + ++chunk->normals.count; + } else { + return rapidobj_errc::ParseError; + } + break; + } + case 'f': { + if (StartsWith(line, "f ") || StartsWith(line, "f\t")) { + line.remove_prefix(2); + auto [count, rc] = ParseFace( + line, + chunk->positions.count, + chunk->texcoords.count, + chunk->normals.count, + kMinVerticesInFace, + kMaxVerticesInFace, + static_cast(ApplyOffset::All), + &chunk->mesh.indices.buffer, + &chunk->mesh.indices.flags); + if (rc != rapidobj_errc::Success) { + return rc; + } + chunk->mesh.faces.buffer.ensure_enough_room_for(1); + chunk->mesh.faces.buffer.push_back(static_cast(count)); + ++chunk->mesh.faces.count; + } + break; + } + case '#': break; // ignore comments + case 'g': + case 'o': { + if (StartsWith(line, "g ") || StartsWith(line, "g\t") || StartsWith(line, "o ") || StartsWith(line, "o\t")) { + line.remove_prefix(2); + auto name = std::string(line); + Trim(name); + chunk->shapes.list.push_back({}); + chunk->shapes.list.back().name = std::move(name); + chunk->shapes.list.back().mesh.index_buffer_start = chunk->mesh.indices.buffer.size(); + chunk->shapes.list.back().mesh.face_buffer_start = chunk->mesh.faces.buffer.size(); + chunk->shapes.list.back().lines.index_buffer_start = chunk->lines.indices.buffer.size(); + chunk->shapes.list.back().lines.segment_buffer_start = chunk->lines.segments.buffer.size(); + chunk->shapes.list.back().points.index_buffer_start = chunk->points.indices.buffer.size(); + } else { + return rapidobj_errc::ParseError; + } + break; + } + case 'm': { + if (StartsWith(line, "mtllib ") || StartsWith(line, "mtllib\t")) { + if (context->material.library) { + line.remove_prefix(7); + Trim(line); + bool ambiguous_material_library = false; + if (context->material.library_name.empty()) { + std::lock_guard lock(context->material.mutex); + if (context->material.library_name.empty()) { + context->material.library_name = line; + context->material.parse_result = std::async(std::launch::async, ParseMaterialLibrary, context); + } else if (context->material.library_name != line) { + ambiguous_material_library = true; + } + } else if (context->material.library_name != line) { + ambiguous_material_library = true; + } + if (ambiguous_material_library) { + return rapidobj_errc::AmbiguousMaterialLibraryError; + } + } + } else { + return rapidobj_errc::ParseError; + } + break; + } + case 'u': { + if (StartsWith(line, "usemtl ") || StartsWith(line, "usemtl\t")) { + if (context->material.library) { + line.remove_prefix(7); + chunk->materials.list.push_back( + { std::string(line), std::string(text), chunk->text.line_count, chunk->mesh.faces.buffer.size() }); + } + } else { + return rapidobj_errc::ParseError; + } + break; + } + case 's': { + if (StartsWith(line, "s ") || StartsWith(line, "s\t")) { + line.remove_prefix(2); + Trim(line); + if (line.empty()) { + return rapidobj_errc::ParseError; + } else if (line == "off") { + chunk->smoothing.list.push_back({ 0, chunk->mesh.faces.buffer.size() }); + } else { + auto value = 0U; + auto data = &line[0]; + if (auto [p, e] = std::from_chars(data, data + line.size(), value); e != kSuccess) { + return rapidobj_errc::ParseError; + } + chunk->smoothing.list.push_back({ value, chunk->mesh.faces.buffer.size() }); + } + } else { + return rapidobj_errc::ParseError; + } + break; + } + case 'l': { + if (StartsWith(line, "l ") || StartsWith(line, "l\t")) { + line.remove_prefix(2); + auto [count, rc] = ParseFace( + line, + chunk->positions.count, + chunk->texcoords.count, + chunk->normals.count, + kMinVerticesInLine, + kMaxVerticesInLine, + static_cast(ApplyOffset::Position | ApplyOffset::Texcoord), + &chunk->lines.indices.buffer, + &chunk->lines.indices.flags); + if (rc != rapidobj_errc::Success) { + return rc; + } + chunk->lines.segments.buffer.ensure_enough_room_for(1); + chunk->lines.segments.buffer.push_back(static_cast(count)); + ++chunk->lines.segments.count; + } + break; + } + case 'p': { + if (StartsWith(line, "p ") || StartsWith(line, "p\t")) { + line.remove_prefix(2); + auto [count, rc] = ParseFace( + line, + chunk->positions.count, + chunk->texcoords.count, + chunk->normals.count, + kMinVerticesInPoint, + kMaxVerticesInPoint, + static_cast(ApplyOffset::Position), + &chunk->points.indices.buffer, + &chunk->points.indices.flags); + if (rc != rapidobj_errc::Success) { + return rc; + } + } + break; + } + default: { + return rapidobj_errc::ParseError; + } + } + + return rapidobj_errc::Success; +} + +inline void ProcessBlocksImpl( + Reader* reader, + size_t block_begin, + size_t block_end, + bool stop_parsing_after_eol, + Chunk* chunk, + SharedContext* context) +{ + assert(reader); + + bool begin_parsing_after_eol = block_begin > 0; + bool reached_eof = false; + + auto buffer_size = kMaxLineLength + kBlockSize; + auto buffer1 = std::unique_ptr(sys::AlignedAllocate(buffer_size, 4_KiB)); + auto buffer2 = std::unique_ptr(sys::AlignedAllocate(buffer_size, 4_KiB)); + + auto front_buffer = buffer1.get(); + auto back_buffer = buffer2.get(); + + auto file_offset = block_begin * kBlockSize; + + auto line = std::string_view(); + auto text = std::string_view(); + + if (auto ec = reader->ReadBlock(file_offset, kBlockSize, front_buffer + kMaxLineLength)) { + chunk->error = Error{ ec }; + return; + } + + if (auto [bytes_read, ec] = reader->WaitForResult(); ec) { + chunk->error = Error{ ec }; + return; + } else { + reached_eof = bytes_read < kBlockSize; + text = std::string_view(front_buffer + kMaxLineLength, bytes_read); + } + + if (begin_parsing_after_eol) { + if (auto ptr = static_cast(memchr(text.data(), '\n', kMaxLineLength))) { + auto pos = static_cast(ptr - text.data()); + text.remove_prefix(pos + 1); + } else { + ++chunk->text.line_count; + auto ec = make_error_code(rapidobj_errc::LineTooLongError); + chunk->error = Error{ ec, std::string(text, 0, kMaxLineLength), chunk->text.line_count }; + return; + } + } + + for (size_t i = block_begin; i != block_end; ++i) { + auto remainder = size_t{}; + + bool last_block = (i + 1 == block_end) || reached_eof; + + if (!last_block) { + file_offset = (i + 1) * kBlockSize; + + if (auto ec = reader->ReadBlock(file_offset, kBlockSize, back_buffer + kMaxLineLength)) { + chunk->error = Error{ ec }; + return; + } + + } else if (stop_parsing_after_eol) { + if (auto ptr = static_cast(memchr(text.data(), '\n', kMaxLineLength))) { + auto pos = static_cast(ptr - text.data()); + line = text.substr(0, pos); + if (EndsWith(line, '\r')) { + line.remove_suffix(1); + } + ++chunk->text.line_count; + if (auto rc = ProcessLine(line, chunk, context); rc != rapidobj_errc::Success) { + chunk->error = Error{ make_error_code(rc), std::string(line), chunk->text.line_count }; + } + } else { + ++chunk->text.line_count; + auto ec = make_error_code(rapidobj_errc::LineTooLongError); + chunk->error = Error{ ec, std::string(text, 0, kMaxLineLength), chunk->text.line_count }; + } + return; + } + + while (!text.empty()) { + if (auto ptr = static_cast(memchr(text.data(), '\n', text.size()))) { + auto pos = static_cast(ptr - text.data()); + ++chunk->text.line_count; + if (pos > kMaxLineLength) { + auto ec = make_error_code(rapidobj_errc::LineTooLongError); + chunk->error = Error{ ec, std::string(text, 0, kMaxLineLength), chunk->text.line_count }; + return; + } + line = text.substr(0, pos); + if (EndsWith(line, '\r')) { + line.remove_suffix(1); + } + text.remove_prefix(pos + 1); + } else { + if (text.size() > kMaxLineLength) { + ++chunk->text.line_count; + auto ec = make_error_code(rapidobj_errc::LineTooLongError); + chunk->error = Error{ ec, std::string(text, 0, kMaxLineLength), chunk->text.line_count }; + return; + } + if (last_block) { + line = text; + ++chunk->text.line_count; + if (auto rc = ProcessLine(line, chunk, context); rc != rapidobj_errc::Success) { + chunk->error = Error{ make_error_code(rc), std::string(line), chunk->text.line_count }; + return; + } + } else { + remainder = text.size(); + memcpy(back_buffer + kMaxLineLength - remainder, text.data(), remainder); + } + text = {}; + break; + } + + if (auto rc = ProcessLine(line, chunk, context); rc != rapidobj_errc::Success) { + chunk->error = Error{ make_error_code(rc), std::string(line), chunk->text.line_count }; + return; + } + } + + if (!last_block) { + auto [bytes_read, ec] = reader->WaitForResult(); + if (ec) { + chunk->error = Error{ ec }; + return; + } + reached_eof = bytes_read < kBlockSize; + std::swap(front_buffer, back_buffer); + text = std::string_view(front_buffer + kMaxLineLength - remainder, bytes_read + remainder); + } else if (reached_eof) { + break; + } + } +} + +inline void ProcessBlocks( + DataSource source, + size_t thread_index, + size_t block_begin, + size_t block_end, + bool stop_parsing_after_eol, + Chunk* chunk, + std::shared_ptr context) +{ + assert(chunk); + assert(context); + assert(block_begin < block_end); + + auto t1 = std::chrono::steady_clock::now(); + + auto reader = CreateReader(source); + + if (reader->Error()) { + chunk->error = Error{ reader->Error() }; + } else { + ProcessBlocksImpl(reader.get(), block_begin, block_end, stop_parsing_after_eol, chunk, context.get()); + } + + if (1 == std::atomic_fetch_sub(&context->parsing.thread_count, size_t(1))) { + context->parsing.completed.set_value(); + } + + auto t2 = std::chrono::steady_clock::now(); + + auto parse_time = t2 - t1; + + context->debug.io.num_requests[thread_index] = reader->NumRequests(); + context->debug.io.num_bytes_read[thread_index] = reader->BytesRead(); + context->debug.io.submit_time[thread_index] = reader->SubmitTime(); + context->debug.io.wait_time[thread_index] = reader->WaitTime(); + context->debug.parse.time[thread_index] = parse_time; +} + +inline void ParseFileSequential(sys::File* file, std::vector* chunks, std::shared_ptr context) +{ + context->thread.concurrency = 1; + context->parsing.thread_count = 1; + + chunks->resize(1); + + context->debug.io.num_requests.resize(1); + context->debug.io.num_bytes_read.resize(1); + context->debug.io.submit_time.resize(1); + context->debug.io.wait_time.resize(1); + context->debug.parse.time.resize(1); + + auto source = DataSource(file); + + auto num_blocks = file->size() / kBlockSize + (file->size() % kBlockSize != 0); + auto stop_parsing_after_eol = false; + auto chunk = &chunks->front(); + + ProcessBlocks(source, 0, 0, num_blocks, stop_parsing_after_eol, chunk, context); +} + +inline void ParseFileParallel(sys::File* file, std::vector* chunks, std::shared_ptr context) +{ + auto source = DataSource(file); + auto num_blocks = file->size() / kBlockSize + (file->size() % kBlockSize != 0); + auto num_threads = std::thread::hardware_concurrency(); + auto num_blocks_per_thread = num_blocks / num_threads; + auto num_remainder_blocks = num_blocks - (num_blocks_per_thread * num_threads); + + auto tasks = std::vector(); + + tasks.reserve(num_threads); + + // allocate blocks to tasks + { + auto block = size_t{}; + + while (block < num_blocks) { + tasks.push_back(block); + block += num_blocks_per_thread; + if (num_remainder_blocks > 0) { + ++block; + --num_remainder_blocks; + } + } + } + + auto num_tasks = tasks.size(); + auto threads = std::vector{}; + + chunks->resize(num_tasks); + + context->thread.concurrency = num_threads; + context->parsing.thread_count = num_tasks; + + context->debug.io.num_requests.resize(num_threads); + context->debug.io.num_bytes_read.resize(num_threads); + context->debug.io.submit_time.resize(num_threads); + context->debug.io.wait_time.resize(num_threads); + context->debug.parse.time.resize(num_threads); + + threads.reserve(num_tasks); + + // allocate tasks to threads + for (size_t i = 0; i != tasks.size(); ++i) { + bool is_last = i + 1 == tasks.size(); + auto begin = tasks[i]; + auto end = is_last ? num_blocks : (tasks[i + 1] + 1); + bool stop_parsing_after_eol = !is_last; + auto chunk = &(*chunks)[i]; + + threads.emplace_back(ProcessBlocks, source, i, begin, end, stop_parsing_after_eol, chunk, context); + threads.back().detach(); + } + + // wait for parsing to finish + context->parsing.completed.get_future().wait(); +} + +inline Result ParseFile(const std::filesystem::path& filepath, const MaterialLibrary& material_library) +{ + if (filepath.empty()) { + auto error = std::make_error_code(std::errc::invalid_argument); + return Result{ Attributes{}, Shapes{}, Materials{}, Error{ error } }; + } + + auto file = sys::File(filepath); + + if (!file) { + return Result{ Attributes{}, Shapes{}, Materials{}, Error{ file.error() } }; + } + + const auto material_library_value = &material_library.Value(); + const auto default_loading_policy = material_library.Policy().value_or(Load::Mandatory); + const auto default_material_library = MaterialLibrary::SearchPath(".", default_loading_policy); + + auto context = std::make_shared(); + + context->material.basepath = filepath.parent_path(); + + if (std::get_if(material_library_value) != nullptr) { + context->material.library = nullptr; + } else if (std::get_if(material_library_value) != nullptr) { + context->material.library = &default_material_library; + } else if (auto* paths = std::get_if>(material_library_value)) { + if (paths->empty()) { + return Result{ Attributes{}, Shapes{}, Materials{}, Error{ rapidobj_errc::InvalidArgumentsError } }; + } + context->material.library = &material_library; + } else if (std::get_if(material_library_value) != nullptr) { + context->material.library = &material_library; + } else { + return Result{ Attributes{}, Shapes{}, Materials{}, Error{ rapidobj_errc::InternalError } }; + } + + auto chunks = std::vector(); + + auto t1 = std::chrono::steady_clock::now(); + + if (file.size() <= kSingleThreadCutoff) { + ParseFileSequential(&file, &chunks, context); + } else { + ParseFileParallel(&file, &chunks, context); + } + + auto t2 = std::chrono::steady_clock::now(); + + context->debug.parse.total_time = t2 - t1; + + // check if an error occured + size_t running_line_num = size_t{}; + for (auto& chunk : chunks) { + if (chunk.error.code) { + chunk.error.line_num += running_line_num; + return Result{ Attributes{}, Shapes{}, Materials{}, chunk.error }; + } + running_line_num += chunk.text.line_count; + } + + t1 = std::chrono::steady_clock::now(); + + auto result = Merge(chunks, context); + + t2 = std::chrono::steady_clock::now(); + + context->debug.merge.total_time = t2 - t1; + + // std::cout << DumpDebug(*context); + + auto memory = size_t{ 0 }; + + for (const auto& chunk : chunks) { + memory += SizeInBytes(chunk); + } + + // Free memory in a different thread + if (memory > kMemoryRecyclingSize) { + auto recycle = std::thread([](std::vector&&) {}, std::move(chunks)); + recycle.detach(); + } + + return result; +} + +inline Result ParseStream(std::istream& is, const MaterialLibrary& material_library) +{ + auto context = std::make_shared(); + auto chunks = std::vector(1); + auto source = DataSource(&is); + auto num_blocks = std::numeric_limits::max(); + auto stop_parsing_after_eol = false; + auto chunk = &chunks.front(); + auto material_library_value = &material_library.Value(); + auto default_material_library = MaterialLibrary::SearchPaths({}, Load::Optional); + + if (std::get_if(material_library_value) != nullptr) { + context->material.library = nullptr; + } else if (std::get_if(material_library_value) != nullptr) { + if (material_library.Policy()) { + return Result{ Attributes{}, Shapes{}, Materials{}, Error{ rapidobj_errc::InvalidArgumentsError } }; + } + context->material.library = &default_material_library; + } else if (auto* paths = std::get_if>(material_library_value)) { + if (paths->empty()) { + return Result{ Attributes{}, Shapes{}, Materials{}, Error{ rapidobj_errc::InvalidArgumentsError } }; + } + if (std::any_of(paths->begin(), paths->end(), [](auto& path) { return path.is_relative(); })) { + return Result{ Attributes{}, Shapes{}, Materials{}, Error{ rapidobj_errc::MaterialRelativePathError } }; + } + context->material.library = &material_library; + } else if (std::get_if(material_library_value) != nullptr) { + context->material.library = &material_library; + } else { + return Result{ Attributes{}, Shapes{}, Materials{}, Error{ rapidobj_errc::InternalError } }; + } + + context->thread.concurrency = 1; + context->parsing.thread_count = 1; + + context->debug.io.num_requests.resize(1); + context->debug.io.num_bytes_read.resize(1); + context->debug.io.submit_time.resize(1); + context->debug.io.wait_time.resize(1); + context->debug.parse.time.resize(1); + + auto t1 = std::chrono::steady_clock::now(); + + ProcessBlocks(source, 0, 0, num_blocks, stop_parsing_after_eol, chunk, context); + + auto t2 = std::chrono::steady_clock::now(); + + context->debug.parse.total_time = t2 - t1; + + if (chunks.front().error.code) { + return Result{ Attributes{}, Shapes{}, Materials{}, chunks.front().error }; + } + + t1 = std::chrono::steady_clock::now(); + + auto result = Merge(chunks, context); + + t2 = std::chrono::steady_clock::now(); + + context->debug.merge.total_time = t2 - t1; + + // std::cout << DumpDebug(*context); + + // Free memory in a different thread + if (SizeInBytes(chunks.front()) > kMemoryRecyclingSize) { + auto recycle = std::thread([](std::vector&&) {}, std::move(chunks)); + recycle.detach(); + } + + return result; +} + +struct TriangulateTask final { + TriangulateTask( + const Mesh* src_, + Mesh* dst_, + size_t cost_, + size_t isrc_, + size_t idst_, + size_t fsrc_, + size_t fdst_, + size_t size_) noexcept + : src(src_), dst(dst_), cost(cost_), isrc(isrc_), idst(idst_), fsrc(fsrc_), fdst(fdst_), size(size_) + {} + + const Mesh* src{}; + Mesh* dst{}; + size_t cost{}; + size_t isrc{}; + size_t idst{}; + size_t fsrc{}; + size_t fdst{}; + size_t size{}; +}; + +inline auto CalculatePolygonArea(float* x, float* y, size_t size) noexcept +{ + auto area = 0.0f; + + for (size_t i = 1; i != size; ++i) { + auto avg_height = (y[i - 1] + y[i]) / 2; + auto width = x[i] - x[i - 1]; + area += width * avg_height; + } + + auto avg_height = (y[0] + y[size - 1]) / 2; + auto width = x[0] - x[size - 1]; + area += width * avg_height; + + return std::abs(area); +} + +enum class ProjectionPlane { X, Y, Z }; + +inline bool TriangulateSingleTask(const Array& positions, const TriangulateTask& task) +{ + auto [src, dst, cost, isrc, idst, fsrc, fdst, size] = task; + + auto complex = std::array>, 1>(); + auto& polygon = complex.front(); + auto index_map = std::vector(); + + for (size_t i = 0; i != size; ++i) { + auto num_vertices = src->num_face_vertices[fsrc + i]; + auto material_id = src->material_ids[fsrc + i]; + auto smoothing_id = src->smoothing_group_ids[fsrc + i]; + + if (num_vertices == 3) { + dst->indices[idst + 0] = src->indices[isrc + 0]; + dst->indices[idst + 1] = src->indices[isrc + 1]; + dst->indices[idst + 2] = src->indices[isrc + 2]; + + isrc += 3; + idst += 3; + + dst->num_face_vertices[fdst] = 3; + dst->material_ids[fdst] = material_id; + dst->smoothing_group_ids[fdst] = smoothing_id; + + fdst++; + + } else if (num_vertices == 4) { + const auto& index0 = src->indices[isrc + 0]; + const auto& index1 = src->indices[isrc + 1]; + const auto& index2 = src->indices[isrc + 2]; + const auto& index3 = src->indices[isrc + 3]; + + isrc += 4; + + bool d02_is_less{}; + { + auto position_index0 = 3 * static_cast(index0.position_index); + auto position_index1 = 3 * static_cast(index1.position_index); + auto position_index2 = 3 * static_cast(index2.position_index); + auto position_index3 = 3 * static_cast(index3.position_index); + + auto pos0_x = positions[position_index0 + 0]; + auto pos0_y = positions[position_index0 + 1]; + auto pos0_z = positions[position_index0 + 2]; + + auto pos1_x = positions[position_index1 + 0]; + auto pos1_y = positions[position_index1 + 1]; + auto pos1_z = positions[position_index1 + 2]; + + auto pos2_x = positions[position_index2 + 0]; + auto pos2_y = positions[position_index2 + 1]; + auto pos2_z = positions[position_index2 + 2]; + + auto pos3_x = positions[position_index3 + 0]; + auto pos3_y = positions[position_index3 + 1]; + auto pos3_z = positions[position_index3 + 2]; + + auto e02_x = pos0_x - pos2_x; + auto e02_y = pos0_y - pos2_y; + auto e02_z = pos0_z - pos2_z; + + auto e13_x = pos1_x - pos3_x; + auto e13_y = pos1_y - pos3_y; + auto e13_z = pos1_z - pos3_z; + + auto d02 = e02_x * e02_x + e02_y * e02_y + e02_z * e02_z; + auto d13 = e13_x * e13_x + e13_y * e13_y + e13_z * e13_z; + + d02_is_less = d02 < d13; + } + + dst->indices[idst + 0] = index0; + dst->indices[idst + 1] = index1; + dst->indices[idst + 2] = d02_is_less ? index2 : index3; + dst->indices[idst + 3] = d02_is_less ? index0 : index1; + dst->indices[idst + 4] = index2; + dst->indices[idst + 5] = index3; + + idst += 6; + + dst->num_face_vertices[fdst + 0] = 3; + dst->num_face_vertices[fdst + 1] = 3; + dst->material_ids[fdst + 0] = material_id; + dst->material_ids[fdst + 1] = material_id; + dst->smoothing_group_ids[fdst + 0] = smoothing_id; + dst->smoothing_group_ids[fdst + 1] = smoothing_id; + + fdst += 2; + } else { + auto xs = std::array(); + auto ys = std::array(); + auto zs = std::array(); + + polygon.clear(); + index_map.clear(); + + for (size_t k = 0; k != num_vertices; ++k) { + auto position_index = 3 * static_cast(src->indices[isrc + k].position_index); + index_map.push_back(isrc + k); + xs[k] = positions[position_index + 0]; + ys[k] = positions[position_index + 1]; + zs[k] = positions[position_index + 2]; + } + + isrc += num_vertices; + + auto area_x = CalculatePolygonArea(ys.data(), zs.data(), num_vertices); + auto area_y = CalculatePolygonArea(xs.data(), zs.data(), num_vertices); + auto area_z = CalculatePolygonArea(xs.data(), ys.data(), num_vertices); + + if (FLT_MIN > std::max({ area_x, area_y, area_z })) { + return false; + } + + auto proj_plane = ProjectionPlane{}; + + if (area_x > area_y) { + proj_plane = area_x > area_z ? ProjectionPlane::X : ProjectionPlane::Z; + } else { + proj_plane = area_y > area_z ? ProjectionPlane::Y : ProjectionPlane::Z; + } + + if (proj_plane == ProjectionPlane::X) { + for (size_t k = 0; k != num_vertices; ++k) { + polygon.push_back({ ys[k], zs[k] }); + } + } else if (proj_plane == ProjectionPlane::Y) { + for (size_t k = 0; k != num_vertices; ++k) { + polygon.push_back({ xs[k], zs[k] }); + } + } else { + for (size_t k = 0; k != num_vertices; ++k) { + polygon.push_back({ xs[k], ys[k] }); + } + } + + auto result = mapbox::earcut(complex); + + if (result.empty() || result.size() % 3 != 0) { + return false; + } + + for (size_t k = 0; k < result.size(); k += 3) { + std::swap(result[k], result[k + 1]); + } + + for (size_t k = 0; k != result.size(); ++k) { + dst->indices[idst + k] = src->indices[index_map[result[k]]]; + } + + idst += result.size(); + + auto num_triangles = result.size() / 3; + + for (size_t k = 0; k != num_triangles; ++k) { + dst->num_face_vertices[fdst + k] = 3; + dst->material_ids[fdst + k] = material_id; + dst->smoothing_group_ids[fdst + k] = smoothing_id; + } + + fdst += num_triangles; + } + } + return true; +} + +inline bool +TriangulateTasksParallel(size_t concurrency, const Array& positions, const std::vector& tasks) +{ + auto task_index = std::atomic_size_t{ 0 }; + auto num_threads = std::atomic_size_t{ concurrency }; + auto completed = std::promise(); + auto success = std::atomic_bool{ true }; + + auto func = [&]() { + auto fetched_index = std::atomic_fetch_add(&task_index, size_t(1)); + + while (fetched_index < tasks.size()) { + if (false == TriangulateSingleTask(positions, tasks[fetched_index])) { + success = false; + break; + } + fetched_index = std::atomic_fetch_add(&task_index, size_t(1)); + } + + if (1 == std::atomic_fetch_sub(&num_threads, size_t(1))) { + completed.set_value(); + } + }; + + auto threads = std::vector(); + threads.reserve(concurrency); + + for (size_t i = 0; i != concurrency; ++i) { + threads.emplace_back(func); + threads.back().detach(); + } + + // wait for triangulation to finish + completed.get_future().wait(); + + return success; +} + +inline bool TriangulateTasksSequential(const Array& positions, const std::vector& tasks) +{ + for (const auto& task : tasks) { + bool success = TriangulateSingleTask(positions, task); + if (!success) { + return false; + } + } + return true; +} + +inline bool Triangulate(Result& result) +{ + auto mesh_tasks = std::vector(); + auto tasks = std::vector(); + auto meshes = std::vector(result.shapes.size()); + + tasks.reserve(result.shapes.size()); + + for (size_t i = 0; i != result.shapes.size(); ++i) { + const auto& mesh = result.shapes[i].mesh; + + mesh_tasks.clear(); + + auto cost = size_t{ 0 }; + + auto isrc_begin = size_t{ 0 }; + auto isrc_end = size_t{ 0 }; + auto idst_begin = size_t{ 0 }; + auto idst_end = size_t{ 0 }; + + auto fsrc_begin = size_t{ 0 }; + auto fsrc_end = size_t{ 0 }; + auto fdst_begin = size_t{ 0 }; + auto fdst_end = size_t{ 0 }; + + auto triangle_sum = size_t{ 0 }; + + for (auto num_vertices : mesh.num_face_vertices) { + auto num_triangles = static_cast(num_vertices - 2); + + triangle_sum += num_triangles; + + if (num_vertices == 3) { + cost += kTriangulateTriangleCost; + } else if (num_vertices == 4) { + cost += kTriangulateQuadCost; + } else { + auto n = static_cast(num_vertices); + cost += kTriangulatePerIndexCost * n * n; + } + if (cost >= kTriangulateSubdivideCost) { + auto size = fsrc_end - fsrc_begin; + mesh_tasks.emplace_back(&mesh, nullptr, cost, isrc_begin, idst_begin, fsrc_begin, fdst_begin, size); + + isrc_begin = isrc_end; + idst_begin = idst_end; + fsrc_begin = fsrc_end; + fdst_begin = fdst_end; + + cost = 0; + } + + isrc_end += num_vertices; + idst_end += 3 * num_triangles; + fsrc_end += 1; + fdst_end += num_triangles; + } + + if (triangle_sum == mesh.num_face_vertices.size()) { + continue; + } + + if (auto size = fsrc_end - fsrc_begin; size > 0) { + mesh_tasks.emplace_back(&mesh, nullptr, cost, isrc_begin, idst_begin, fsrc_begin, fdst_begin, size); + } + + meshes[i].indices = Array(3 * triangle_sum); + meshes[i].num_face_vertices = Array(triangle_sum); + meshes[i].material_ids = Array(triangle_sum); + meshes[i].smoothing_group_ids = Array(triangle_sum); + + for (auto& task : mesh_tasks) { + task.dst = &meshes[i]; + } + + tasks.insert(tasks.end(), mesh_tasks.begin(), mesh_tasks.end()); + } + + // already triangulated + if (tasks.empty()) { + return true; + } + + auto hardware_threads = static_cast(std::thread::hardware_concurrency()); + auto concurrency = std::min(hardware_threads, tasks.size()); + bool success = true; + + if (concurrency > 1) { + success = TriangulateTasksParallel(concurrency, result.attributes.positions, tasks); + } else { + success = TriangulateTasksSequential(result.attributes.positions, tasks); + } + + if (!success) { + result.error = Error{ make_error_code(rapidobj_errc::TriangulationError), {}, {} }; + } + + auto old_meshes = std::vector(); + old_meshes.reserve(result.shapes.size()); + + for (size_t i = 0; i != result.shapes.size(); ++i) { + if (!meshes[i].indices.empty()) { + old_meshes.push_back(std::move(result.shapes[i].mesh)); + result.shapes[i].mesh = std::move(meshes[i]); + } + } + + auto memory = size_t{ 0 }; + + for (const auto& old_mesh : old_meshes) { + memory += SizeInBytes(old_mesh); + } + + // Free memory in a different thread + if (memory > kMemoryRecyclingSize) { + auto recycle = std::thread([](std::vector&&) {}, std::move(old_meshes)); + recycle.detach(); + } + + return success; +} + +} // namespace detail + +/// +/// Loads and parses Wavefront geometry definition file (.obj file). +/// +/// : path of the .obj file to parse. +/// : optional material library. +/// Parsed data stored in Result class. +inline Result ParseFile(const std::filesystem::path& obj_filepath, const MaterialLibrary& mtl_library) +{ + return detail::ParseFile(obj_filepath, mtl_library); +} + +/// +/// Loads and parses Wavefront geometry definition data from an input stream. +/// Because input streams are sequential, which prevents parsing parallelization, +/// this function is less performant compared to the ParseFile() function. +/// +/// : input stream to parse. +/// : optional material library. +/// Parsed data stored in Result class. +inline Result ParseStream(std::istream& obj_stream, const MaterialLibrary& mtl_library) +{ + return detail::ParseStream(obj_stream, mtl_library); +} + +inline bool Triangulate(Result& result) +{ + return detail::Triangulate(result); +} + +} // namespace rapidobj + +#endif diff --git a/src/testrender/raytracer.h b/src/testrender/raytracer.h index 5308f1870..cf640a8cb 100644 --- a/src/testrender/raytracer.h +++ b/src/testrender/raytracer.h @@ -13,6 +13,7 @@ #include "render_params.h" #include #include +#include "bvh.h" #if OSL_USE_OPTIX @@ -149,346 +150,143 @@ struct Camera { float invw, invh; }; - - -// Note: The primitives only use the intersection routines, etc., for CPU. -// They aren't used for OptiX mode, where instead the equivalent are in -// the cuda subdirectory. - - -struct Primitive { - Primitive(int shaderID, bool isLight) : shaderID(shaderID), isLight(isLight) - { - } - virtual ~Primitive() {} - - int shaderid() const { return shaderID; } - bool islight() const { return isLight; } - void getBounds(float& minx, float& miny, float& minz, float& maxx, - float& maxy, float& maxz) const; - -#if OSL_USE_OPTIX - virtual void setOptixVariables(void* data) const = 0; -#endif - -private: - int shaderID; - bool isLight; +struct TriangleIndices { + int a, b, c; }; - -struct Sphere final : public Primitive { - Sphere(Vec3 c, float r, int shaderID, bool isLight) - : Primitive(shaderID, isLight), c(c), r(r), r2(r * r) - { - OSL_DASSERT(r > 0); - } - - void getBounds(float& minx, float& miny, float& minz, float& maxx, - float& maxy, float& maxz) const - { - minx = c.x - r; - miny = c.y - r; - minz = c.z - r; - maxx = c.x + r; - maxy = c.y + r; - maxz = c.z + r; - } - - // returns distance to nearest hit or 0 - Dual2 intersect(const Ray& r, bool self) const - { - Dual2 oc = c - r.origin; - Dual2 b = dot(oc, r.direction); - Dual2 det = b * b - dot(oc, oc) + r2; - if (det.val() >= 0) { - det = sqrt(det); - Dual2 x = b - det; - Dual2 y = b + det; - return self ? (fabsf(x.val()) > fabsf(y.val()) - ? (x.val() > 0 ? x : 0) - : (y.val() > 0 ? y : 0)) - : (x.val() > 0 ? x : (y.val() > 0 ? y : 0)); - } - return 0; // no hit - } - - float surfacearea() const { return float(M_PI) * r2; } - - Dual2 normal(const Dual2& p) const { return normalize(p - c); } - - Dual2 uv(const Dual2& /*p*/, const Dual2& n, Vec3& dPdu, - Vec3& dPdv) const - { - Dual2 nx(n.val().x, n.dx().x, n.dy().x); - Dual2 ny(n.val().y, n.dx().y, n.dy().y); - Dual2 nz(n.val().z, n.dx().z, n.dy().z); - Dual2 u = (atan2(nx, nz) + Dual2(M_PI)) * 0.5f - * float(M_1_PI); - Dual2 v = safe_acos(ny) * float(M_1_PI); - // Review of sphere parameterization: - // x = r * -sin(2pi*u) * sin(pi*v) - // y = r * cos(pi*v) - // z = r * -cos(2pi*u) * sin(pi*v) - // partial derivs: - // dPdu.x = -r * sin(pi*v) * 2pi * cos(2pi*u) - // dPdu.y = 0 - // dPdu.z = r * sin(pi*v) * 2pi * sin(2pi*u) - // dPdv.x = r * -cos(pi*v) * pi * sin(2pi*u) - // dPdv.y = r * -pi * sin(pi*v) - // dPdv.z = r * -cos(pi*v) * pi * cos(2pi*u) - const float pi = float(M_PI); - float twopiu = 2.0f * pi * u.val(); - float sin2piu, cos2piu; - OIIO::sincos(twopiu, &sin2piu, &cos2piu); - float sinpiv, cospiv; - OIIO::sincos(pi * v.val(), &sinpiv, &cospiv); - float pir = pi * r; - dPdu.x = -2.0f * pir * sinpiv * cos2piu; - dPdu.y = 0.0f; - dPdu.z = 2.0f * pir * sinpiv * sin2piu; - dPdv.x = -pir * cospiv * sin2piu; - dPdv.y = -pir * sinpiv; - dPdv.z = -pir * cospiv * cos2piu; - return make_Vec2(u, v); - } - - // return a direction towards a point on the sphere - Vec3 sample(const Vec3& x, float xi, float yi, float& pdf) const - { - const float TWOPI = float(2 * M_PI); - float cmax2 = 1 - r2 / (c - x).length2(); - float cmax = cmax2 > 0 ? sqrtf(cmax2) : 0; - float cos_a = 1 - xi + xi * cmax; - float sin_a = sqrtf(1 - cos_a * cos_a); - float phi = TWOPI * yi; - float sp, cp; - OIIO::fast_sincos(phi, &sp, &cp); - Vec3 sw = (c - x).normalize(), su, sv; - ortho(sw, su, sv); - pdf = 1 / (TWOPI * (1 - cmax)); - return (su * (cp * sin_a) + sv * (sp * sin_a) + sw * cos_a).normalize(); - } - - float shapepdf(const Vec3& x, const Vec3& /*p*/) const - { - const float TWOPI = float(2 * M_PI); - float cmax2 = 1 - r2 / (c - x).length2(); - float cmax = cmax2 > 0 ? sqrtf(cmax2) : 0; - return 1 / (TWOPI * (1 - cmax)); - } - -#if OSL_USE_OPTIX - virtual void setOptixVariables(void* data) const - { - SphereParams* sphere_data = reinterpret_cast(data); - sphere_data->c = make_float3(c.x, c.y, c.z); - sphere_data->r2 = r2; - sphere_data->a = M_PI * (r2 * r2); - sphere_data->shaderID = shaderid(); - } -#endif - -private: - Vec3 c; - float r, r2; +struct LightSample { + Vec3 dir; + float dist; + float pdf; + float u, v; }; +using ShaderMap = std::unordered_map; +struct Scene { + void add_sphere(const Vec3& c, float r, int shaderID, int resolution); -struct Quad final : public Primitive { - Quad(const Vec3& p, const Vec3& ex, const Vec3& ey, int shaderID, - bool isLight) - : Primitive(shaderID, isLight), p(p), ex(ex), ey(ey) - { - n = ex.cross(ey); - a = n.length(); - n = n.normalize(); - eu = 1 / ex.length2(); - ev = 1 / ey.length2(); - } + void add_quad(const Vec3& p, const Vec3& ex, const Vec3& ey, int shaderID, + int resolution); - void getBounds(float& minx, float& miny, float& minz, float& maxx, - float& maxy, float& maxz) const - { - const Vec3 p0 = p; - const Vec3 p1 = p + ex; - const Vec3 p2 = p + ex + ey; - const Vec3 p3 = p + ey; - minx = std::min(p0.x, std::min(p1.x, std::min(p2.x, p3.x))); - miny = std::min(p0.y, std::min(p1.y, std::min(p2.y, p3.y))); - minz = std::min(p0.z, std::min(p1.z, std::min(p2.z, p3.z))); - maxx = std::max(p0.x, std::max(p1.x, std::max(p2.x, p3.x))); - maxy = std::max(p0.y, std::max(p1.y, std::max(p2.y, p3.y))); - maxz = std::max(p0.z, std::max(p1.z, std::max(p2.z, p3.z))); - } + // add models parsed from a .obj file + void add_model(const std::string& filename, const ShaderMap& shadermap, + int shaderID, OIIO::ErrorHandler& errhandler); - // returns distance to nearest hit or 0 - Dual2 intersect(const Ray& r, bool self) const - { - if (self) - return 0; - Dual2 dn = dot(r.direction, n); - Dual2 en = dot(p - r.origin, n); - if (dn.val() * en.val() > 0) { - Dual2 t = en / dn; - Dual2 h = r.point(t) - p; - Dual2 dx = dot(h, ex) * eu; - Dual2 dy = dot(h, ey) * ev; - if (dx.val() >= 0 && dx.val() < 1 && dy.val() >= 0 && dy.val() < 1) - return t; - } - return 0; // no hit - } + int num_prims() const { return triangles.size(); } - float surfacearea() const { return a; } + void prepare(OIIO::ErrorHandler& errhandler); - Dual2 normal(const Dual2& /*p*/) const - { - return Dual2(n, Vec3(0, 0, 0), Vec3(0, 0, 0)); - } + Intersection intersect(const Ray& r, const float tmax, + const unsigned skipID1, + const unsigned skipID2 = ~0u) const; - Dual2 uv(const Dual2& p, const Dual2& /*n*/, Vec3& dPdu, - Vec3& dPdv) const + LightSample sample(int primID, const Vec3& x, float xi, float yi) const { - Dual2 h = p - this->p; - Dual2 u = dot(h, ex) * eu; - Dual2 v = dot(h, ey) * ev; - dPdu = ex; - dPdv = ey; - return make_Vec2(u, v); - } + // A Low-Distortion Map Between Triangle and Square + // Eric Heitz, 2019 + if (yi > xi) { + xi *= 0.5f; + yi -= xi; + } else { + yi *= 0.5f; + xi -= yi; + } + const Vec3 va = verts[triangles[primID].a]; + const Vec3 vb = verts[triangles[primID].b]; + const Vec3 vc = verts[triangles[primID].c]; + const Vec3 n = (va - vb).cross(va - vc); - // return a direction towards a point on the sphere - Vec3 sample(const Vec3& x, float xi, float yi, float& pdf) const - { - Vec3 l = (p + xi * ex + yi * ey) - x; + Vec3 l = ((1 - xi - yi) * va + xi * vb + yi * vc) - x; float d2 = l.length2(); Vec3 dir = l.normalize(); - pdf = d2 / (a * fabsf(dir.dot(n))); - return dir; + // length of n is twice the area + float pdf = d2 / (0.5f * fabsf(dir.dot(n))); + return { dir, sqrtf(d2), pdf, xi, yi }; } - float shapepdf(const Vec3& x, const Vec3& p) const + float shapepdf(int primID, const Vec3& x, const Vec3& p) const { + const Vec3 va = verts[triangles[primID].a]; + const Vec3 vb = verts[triangles[primID].b]; + const Vec3 vc = verts[triangles[primID].c]; + const Vec3 n = (va - vb).cross(va - vc); + Vec3 l = p - x; float d2 = l.length2(); Vec3 dir = l.normalize(); - return d2 / (a * fabsf(dir.dot(n))); - } - -#if OSL_USE_OPTIX - virtual void setOptixVariables(void* data) const - { - QuadParams* quad_data = reinterpret_cast(data); - quad_data->p = make_float3(p.x, p.y, p.z); - quad_data->ex = make_float3(ex.x, ex.y, ex.z); - quad_data->ey = make_float3(ey.x, ey.y, ey.z); - quad_data->n = make_float3(n.x, n.y, n.z); - quad_data->eu = eu; - quad_data->ev = ev; - quad_data->a = a; - quad_data->shaderID = shaderid(); - } -#endif - -private: - Vec3 p, ex, ey, n; - float a, eu, ev; -}; - - - -struct Scene { - void add_sphere(const Sphere& s) { spheres.push_back(s); } - - void add_quad(const Quad& q) { quads.push_back(q); } - - int num_prims() const { return spheres.size() + quads.size(); } - - bool intersect(const Ray& r, Dual2& t, int& primID) const - { - const int ns = spheres.size(); - const int nq = quads.size(); - const int self = primID; // remember which object we started from - t = std::numeric_limits::infinity(); - primID = -1; // reset ID - for (int i = 0; i < ns; i++) { - Dual2 d = spheres[i].intersect(r, self == i); - if (d.val() > 0 && d.val() < t.val()) { // found valid hit? - t = d; - primID = i; - } - } - for (int i = 0; i < nq; i++) { - Dual2 d = quads[i].intersect(r, self == (i + ns)); - if (d.val() > 0 && d.val() < t.val()) { // found valid hit? - t = d; - primID = i + ns; - } - } - return primID >= 0; + // length of n is twice the area + return d2 / (0.5f * fabsf(dir.dot(n))); } - Vec3 sample(int primID, const Vec3& x, float xi, float yi, float& pdf) const - { - if (primID < int(spheres.size())) - return spheres[primID].sample(x, xi, yi, pdf); - primID -= spheres.size(); - return quads[primID].sample(x, xi, yi, pdf); - } - - float shapepdf(int primID, const Vec3& x, const Vec3& p) const + float primitivearea(int primID) const { - if (primID < int(spheres.size())) - return spheres[primID].shapepdf(x, p); - primID -= spheres.size(); - return quads[primID].shapepdf(x, p); + const Vec3 va = verts[triangles[primID].a]; + const Vec3 vb = verts[triangles[primID].b]; + const Vec3 vc = verts[triangles[primID].c]; + return 0.5f * (va - vb).cross(va - vc).length(); } - float surfacearea(int primID) const + Vec3 normal(const Dual2& p, Vec3& Ng, int primID, float u, + float v) const { - if (primID < int(spheres.size())) - return spheres[primID].surfacearea(); - primID -= spheres.size(); - return quads[primID].surfacearea(); + const Vec3 va = verts[triangles[primID].a]; + const Vec3 vb = verts[triangles[primID].b]; + const Vec3 vc = verts[triangles[primID].c]; + Ng = (va - vb).cross(va - vc).normalize(); + + // this triangle doesn't have vertex normals, just use face normal + if (n_triangles[primID].a < 0) + return Ng; + + // use vertex normals + const Vec3 na = normals[n_triangles[primID].a]; + const Vec3 nb = normals[n_triangles[primID].b]; + const Vec3 nc = normals[n_triangles[primID].c]; + return ((1 - u - v) * na + u * nb + v * nc).normalize(); } - Dual2 normal(const Dual2& p, int primID) const + Dual2 uv(const Dual2& p, const Vec3& n, Vec3& dPdu, Vec3& dPdv, + int primID, float u, float v) const { - if (primID < int(spheres.size())) - return spheres[primID].normal(p); - primID -= spheres.size(); - return quads[primID].normal(p); - } - - Dual2 uv(const Dual2& p, const Dual2& n, Vec3& dPdu, - Vec3& dPdv, int primID) const - { - if (primID < int(spheres.size())) - return spheres[primID].uv(p, n, dPdu, dPdv); - primID -= spheres.size(); - return quads[primID].uv(p, n, dPdu, dPdv); - } - - int shaderid(int primID) const - { - if (primID < int(spheres.size())) - return spheres[primID].shaderid(); - primID -= spheres.size(); - return quads[primID].shaderid(); - } - - bool islight(int primID) const - { - if (primID < int(spheres.size())) - return spheres[primID].islight(); - primID -= spheres.size(); - return quads[primID].islight(); + if (uv_triangles[primID].a < 0) + return Dual2(Vec2(0, 0)); + const Vec2 ta = uvs[uv_triangles[primID].a]; + const Vec2 tb = uvs[uv_triangles[primID].b]; + const Vec2 tc = uvs[uv_triangles[primID].c]; + + const Vec3 va = verts[triangles[primID].a]; + const Vec3 vb = verts[triangles[primID].b]; + const Vec3 vc = verts[triangles[primID].c]; + + const Vec2 dt02 = ta - tc, dt12 = tb - tc; + const Vec3 dp02 = va - vc, dp12 = vb - vc; + // TODO: could use Kahan's algorithm here + // https://pharr.org/matt/blog/2019/11/03/difference-of-floats + const float det = dt02.x * dt12.y - dt02.y * dt12.x; + if (det != 0) { + Float invdet = 1 / det; + dPdu = (dt12.y * dp02 - dt02.y * dp12) * invdet; + dPdv = (-dt12.x * dp02 + dt02.x * dp12) * invdet; + // TODO: smooth out dPdu and dPdv by storing per vertex tangents + } + return Dual2((1 - u - v) * ta + u * tb + v * tc); } - std::vector spheres; - std::vector quads; + int shaderid(int primID) const { return shaderids[primID]; } + + // basic triangle data + std::vector verts; + std::vector normals; + std::vector uvs; + std::vector triangles; + std::vector uv_triangles; + std::vector n_triangles; + std::vector shaderids; + std::vector + last_index; // one entry per mesh, stores the last triangle index (+1) -- also is the start triangle of the next mesh + // acceleration structure (built over triangles) + std::unique_ptr bvh; }; OSL_NAMESPACE_EXIT diff --git a/src/testrender/scene.cpp b/src/testrender/scene.cpp new file mode 100644 index 000000000..85e1d9851 --- /dev/null +++ b/src/testrender/scene.cpp @@ -0,0 +1,270 @@ +// Copyright Contributors to the Open Shading Language project. +// SPDX-License-Identifier: BSD-3-Clause +// https://github.com/AcademySoftwareFoundation/OpenShadingLanguage + +#include "rapidobj/rapidobj.hpp" +#include "raytracer.h" + +#include +#include +#include + +OSL_NAMESPACE_ENTER + +void +Scene::add_model(const std::string& filename, const ShaderMap& shadermap, + int shaderID, OIIO::ErrorHandler& errhandler) +{ + OIIO::Timer timer; + rapidobj::MaterialLibrary materials = rapidobj::MaterialLibrary::Default( + rapidobj::Load::Optional); + rapidobj::Result obj_file = rapidobj::ParseFile(filename, materials); + if (obj_file.error) { + // we were unable to parse the scene + errhandler.errorfmt("Error while reading {} - {}", filename, + obj_file.error.code.message()); + return; + } + if (!Triangulate(obj_file)) { + errhandler.errorfmt("Unable to triangulate model from {}", filename); + return; + } + std::vector material_ids; + bool use_material_ids = false; + if (!shadermap.empty()) { + // try to match up obj materials with existing shader names in our shadermap + for (auto&& mat : obj_file.materials) { + auto it = shadermap.find(mat.name); + if (it != shadermap.end()) { + errhandler.infofmt("Found material {}", mat.name); + material_ids.emplace_back(it->second); + use_material_ids = true; + } else { + material_ids.emplace_back(shaderID); + errhandler.infofmt("Material {} did not match any shader names", + mat.name); + } + } + } + int nverts = obj_file.attributes.positions.size() / 3; + int base_idx = verts.size(); + verts.reserve(verts.size() + nverts); + for (int i = 0, i3 = 0; i < nverts; i++, i3 += 3) { + float x = obj_file.attributes.positions[i3 + 0]; + float y = obj_file.attributes.positions[i3 + 1]; + float z = obj_file.attributes.positions[i3 + 2]; + verts.emplace_back(x, y, z); + } + int n_base_idx = normals.size(); + int nnorms = obj_file.attributes.normals.size() / 3; + normals.reserve(normals.size() + nnorms); + for (int i = 0, i3 = 0; i < nnorms; i++, i3 += 3) { + float x = obj_file.attributes.normals[i3 + 0]; + float y = obj_file.attributes.normals[i3 + 1]; + float z = obj_file.attributes.normals[i3 + 2]; + normals.emplace_back(x, y, z); + } + int uv_base_idx = uvs.size(); + int nuvs = obj_file.attributes.texcoords.size() / 2; + uvs.reserve(uvs.size() + nuvs); + for (int i = 0, i2 = 0; i < nuvs; i++, i2 += 2) { + float u = obj_file.attributes.texcoords[i2 + 0]; + float v = obj_file.attributes.texcoords[i2 + 1]; + uvs.emplace_back(u, v); + } + int ntris = 0; + for (auto&& shape : obj_file.shapes) { + int shapeShaderID = shaderID; + if (!use_material_ids) { + // if we couldn't find material names to use, try to lookup matching shaders by the mesh name instead + auto it = shadermap.find(shape.name); + if (it != shadermap.end()) { + errhandler.infofmt("Found mesh {}", shape.name); + shapeShaderID = it->second; + } else { + errhandler.infofmt("Mesh {} did not match any shader names", + shape.name); + } + } + OSL_ASSERT(shape.mesh.material_ids.size() == 0 + || shape.mesh.material_ids.size() + == shape.mesh.indices.size() / 3); + for (int i = 0, n = shape.mesh.indices.size(), f = 0; i < n; + i += 3, f++) { + int a = shape.mesh.indices[i + 0].position_index; + int b = shape.mesh.indices[i + 1].position_index; + int c = shape.mesh.indices[i + 2].position_index; + triangles.emplace_back( + TriangleIndices { base_idx + a, base_idx + b, base_idx + c }); + int na = shape.mesh.indices[i + 0].normal_index; + int nb = shape.mesh.indices[i + 1].normal_index; + int nc = shape.mesh.indices[i + 2].normal_index; + // either all are valid, or none are valid + OSL_DASSERT((na < 0 && nb < 0 && nc < 0) + || (na >= 0 && nb >= 0 && nc >= 0)); + n_triangles.emplace_back(TriangleIndices { + na < 0 ? -1 : n_base_idx + na, + na < 0 ? -1 : n_base_idx + nb, + na < 0 ? -1 : n_base_idx + nc, + }); + int ta = shape.mesh.indices[i + 0].texcoord_index; + int tb = shape.mesh.indices[i + 1].texcoord_index; + int tc = shape.mesh.indices[i + 2].texcoord_index; + OSL_DASSERT((ta < 0 && tb < 0 && tc < 0) + || (ta >= 0 && tb >= 0 && tc >= 0)); + uv_triangles.emplace_back(TriangleIndices { + ta < 0 ? -1 : uv_base_idx + ta, ta < 0 ? -1 : uv_base_idx + tb, + ta < 0 ? -1 : uv_base_idx + tc }); + + if (use_material_ids && !shape.mesh.material_ids.empty()) { + // remap the material ID to our indexing + int obj_mat_id = shape.mesh.material_ids[f]; + OSL_ASSERT(obj_mat_id >= 0); + OSL_ASSERT(obj_mat_id < int(material_ids.size())); + shaderids.emplace_back(material_ids[obj_mat_id]); + } else + shaderids.emplace_back(shapeShaderID); + ntris++; + } + last_index.emplace_back(triangles.size()); + } + double loadtime = timer(); + errhandler.infofmt("Parsed {} vertices and {} triangles from {} in {}", + nverts, ntris, filename, + OIIO::Strutil::timeintervalformat(loadtime, 2)); +} + +void +Scene::add_sphere(const Vec3& c, float r, int shaderID, int resolution) +{ + const int W = 2 * resolution; + const int H = resolution; + const int NV = 2 + W * H; // poles + grid = total vertex count + int base_idx = verts.size(); + int n_base_idx = normals.size(); + int t_base_idx = uvs.size(); + // vertices + verts.emplace_back(c + Vec3(0, r, 0)); // pole +z + normals.emplace_back(0, 1, 0); + // W * H grid of points + for (int y = 0; y < H; y++) { + float t = float(y + 0.5f) / float(H); + float z = cosf(t * float(M_PI)); + float q = sqrtf(1 - z * z); + for (int x = 0; x < W; x++) { + // match the previous parameterization + const float a = float(2 * M_PI) * float(x) / float(W); + const Vec3 n(q * -sinf(a), z, q * -cosf(a)); + verts.emplace_back(c + r * n); + normals.emplace_back(n); + } + } + verts.emplace_back(c - Vec3(0, r, 0)); // pole -z + normals.emplace_back(0, -1, 0); + // create rows for the poles (we use triangles instead of quads near the poles, so the top vertex should be evenly spaced) + for (int y = 0; y < 2; y++) + for (int x = 0; x < W; x++) { + float s = float(x + 0.5f) / float(W); + uvs.emplace_back(s, y); + } + // now create the rest of the plane with a regular spacing + for (int y = 0; y < H; y++) + for (int x = 0; x <= W; x++) { + float s = float(x) / float(W); + float t = float(y + 0.5f) / float(H); + uvs.emplace_back(s, t); + } + + for (int x0 = 0, x1 = W - 1; x0 < W; x1 = x0, x0++) { + // tri to pole + triangles.emplace_back( + TriangleIndices { base_idx, base_idx + 1 + x1, base_idx + 1 + x0 }); + n_triangles.emplace_back(TriangleIndices { + n_base_idx, n_base_idx + 1 + x1, n_base_idx + 1 + x0 }); + uv_triangles.emplace_back( + TriangleIndices { t_base_idx + x1, t_base_idx + 2 * W + x1, + t_base_idx + 2 * W + x1 + 1 }); + + shaderids.emplace_back(shaderID); + for (int y = 0; y < H - 1; y++) { + // quads + int i00 = 1 + (x0 + W * (y + 0)); + int i10 = 1 + (x1 + W * (y + 0)); + int i11 = 1 + (x1 + W * (y + 1)); + int i01 = 1 + (x0 + W * (y + 1)); + + triangles.emplace_back(TriangleIndices { + base_idx + i00, base_idx + i10, base_idx + i11 }); + triangles.emplace_back(TriangleIndices { + base_idx + i00, base_idx + i11, base_idx + i01 }); + + n_triangles.emplace_back(TriangleIndices { + n_base_idx + i00, n_base_idx + i10, n_base_idx + i11 }); + n_triangles.emplace_back(TriangleIndices { + n_base_idx + i00, n_base_idx + i11, n_base_idx + i01 }); + + int t00 = 2 * W + x1 + 1 + (W + 1) * (y + 0); + int t10 = 2 * W + x1 + (W + 1) * (y + 0); + int t11 = 2 * W + x1 + (W + 1) * (y + 1); + int t01 = 2 * W + x1 + 1 + (W + 1) * (y + 1); + uv_triangles.emplace_back(TriangleIndices { + t_base_idx + t00, t_base_idx + t10, t_base_idx + t11 }); + uv_triangles.emplace_back(TriangleIndices { + t_base_idx + t00, t_base_idx + t11, t_base_idx + t01 }); + + shaderids.emplace_back(shaderID); + shaderids.emplace_back(shaderID); + } + triangles.emplace_back(TriangleIndices { base_idx + NV - 1, + base_idx + NV - 1 - W + x0, + base_idx + NV - 1 - W + x1 }); + n_triangles.emplace_back( + TriangleIndices { n_base_idx + NV - 1, n_base_idx + NV - 1 - W + x0, + n_base_idx + NV - 1 - W + x1 }); + uv_triangles.emplace_back( + TriangleIndices { t_base_idx + W + x1, + t_base_idx + 2 * W + x1 + 1 + (W + 1) * (H - 1), + t_base_idx + 2 * W + x1 + (W + 1) * (H - 1) }); + shaderids.emplace_back(shaderID); + } + last_index.emplace_back(triangles.size()); +} + +void +Scene::add_quad(const Vec3& p, const Vec3& ex, const Vec3& ey, int shaderID, + int resolution) +{ + // add vertices + int base_idx = verts.size(); + int t_base_idx = uvs.size(); + for (int v = 0; v <= resolution; v++) + for (int u = 0; u <= resolution; u++) { + float s = float(u) / float(resolution); + float t = float(v) / float(resolution); + verts.emplace_back(p + s * ex + t * ey); + uvs.emplace_back(s, t); + } + for (int v = 0; v < resolution; v++) + for (int u = 0; u < resolution; u++) { + int i00 = (u + 0) + (v + 0) * (resolution + 1); + int i10 = (u + 1) + (v + 0) * (resolution + 1); + int i11 = (u + 1) + (v + 1) * (resolution + 1); + int i01 = (u + 0) + (v + 1) * (resolution + 1); + triangles.emplace_back(TriangleIndices { + base_idx + i00, base_idx + i10, base_idx + i11 }); + triangles.emplace_back(TriangleIndices { + base_idx + i00, base_idx + i11, base_idx + i01 }); + n_triangles.emplace_back(TriangleIndices { -1, -1, -1 }); + n_triangles.emplace_back(TriangleIndices { -1, -1, -1 }); + uv_triangles.emplace_back(TriangleIndices { + t_base_idx + i00, t_base_idx + i10, t_base_idx + i11 }); + uv_triangles.emplace_back(TriangleIndices { + t_base_idx + i00, t_base_idx + i11, t_base_idx + i01 }); + shaderids.emplace_back(shaderID); + shaderids.emplace_back(shaderID); + } + last_index.emplace_back(triangles.size()); +} + + +OSL_NAMESPACE_EXIT diff --git a/src/testrender/simpleraytracer.cpp b/src/testrender/simpleraytracer.cpp index 13c92b88f..f62196f93 100644 --- a/src/testrender/simpleraytracer.cpp +++ b/src/testrender/simpleraytracer.cpp @@ -4,6 +4,7 @@ #include #include +#include #include @@ -185,7 +186,6 @@ strtobool(const char* str) || strcmp(str, "yes") == 0; } - template struct ParamStorage { ParamStorage() : fparamindex(0), iparamindex(0), sparamindex(0) {} @@ -267,6 +267,7 @@ parse_prefix_and_ints(string_view str, string_view prefix, int nvals, int* vals) void SimpleRaytracer::parse_scene_xml(const std::string& scenefile) { + ShaderMap shadermap; pugi::xml_document doc; pugi::xml_parse_result parse_result; if (OIIO::Strutil::ends_with(scenefile, ".xml") @@ -323,17 +324,21 @@ SimpleRaytracer::parse_scene_xml(const std::string& scenefile) // load sphere pugi::xml_attribute center_attr = node.attribute("center"); pugi::xml_attribute radius_attr = node.attribute("radius"); + if (center_attr && radius_attr) { Vec3 center = strtovec(center_attr.value()); float radius = OIIO::Strutil::from_string( radius_attr.value()); if (radius > 0) { - pugi::xml_attribute light_attr = node.attribute("is_light"); - bool is_light = light_attr ? strtobool(light_attr.value()) - : false; - scene.add_sphere(Sphere(center, radius, - int(shaders().size()) - 1, - is_light)); + pugi::xml_attribute resolution_attr = node.attribute( + "resolution"); + int resolution = 64; + if (resolution_attr) { + OIIO::string_view str = resolution_attr.value(); + OIIO::Strutil::parse_int(str, resolution); + } + scene.add_sphere(center, radius, int(shaders().size()) - 1, + resolution); } } } else if (strcmp(node.name(), "Quad") == 0) { @@ -342,14 +347,40 @@ SimpleRaytracer::parse_scene_xml(const std::string& scenefile) pugi::xml_attribute edge_x_attr = node.attribute("edge_x"); pugi::xml_attribute edge_y_attr = node.attribute("edge_y"); if (corner_attr && edge_x_attr && edge_y_attr) { - pugi::xml_attribute light_attr = node.attribute("is_light"); - bool is_light = light_attr ? strtobool(light_attr.value()) - : false; - Vec3 co = strtovec(corner_attr.value()); - Vec3 ex = strtovec(edge_x_attr.value()); - Vec3 ey = strtovec(edge_y_attr.value()); - scene.add_quad( - Quad(co, ex, ey, int(shaders().size()) - 1, is_light)); + Vec3 co = strtovec(corner_attr.value()); + Vec3 ex = strtovec(edge_x_attr.value()); + Vec3 ey = strtovec(edge_y_attr.value()); + + int resolution = 1; + pugi::xml_attribute resolution_attr = node.attribute( + "resolution"); + if (resolution_attr) { + OIIO::string_view str = resolution_attr.value(); + OIIO::Strutil::parse_int(str, resolution); + } + + scene.add_quad(co, ex, ey, int(shaders().size()) - 1, + resolution); + } + } else if (strcmp(node.name(), "Model") == 0) { + // load .obj model + pugi::xml_attribute filename_attr = node.attribute("filename"); + if (filename_attr) { + std::string filename = filename_attr.value(); + std::vector searchpath; + searchpath.emplace_back( + OIIO::Filesystem::parent_path(scenefile)); + std::string actual_filename + = OIIO::Filesystem::searchpath_find(filename, searchpath, + false); + if (actual_filename.empty()) { + errhandler().errorfmt("Unable to find model file {}", + filename); + } else { + // we got a valid filename, try to load the model + scene.add_model(actual_filename, shadermap, + int(shaders().size() - 1), errhandler()); + } } } else if (strcmp(node.name(), "Background") == 0) { pugi::xml_attribute res_attr = node.attribute("resolution"); @@ -359,7 +390,8 @@ SimpleRaytracer::parse_scene_xml(const std::string& scenefile) backgroundShaderID = int(shaders().size()) - 1; } else if (strcmp(node.name(), "ShaderGroup") == 0) { ShaderGroupRef group; - pugi::xml_attribute name_attr = node.attribute("name"); + pugi::xml_attribute is_light_attr = node.attribute("is_light"); + pugi::xml_attribute name_attr = node.attribute("name"); std::string name = name_attr ? name_attr.value() : "group"; pugi::xml_attribute type_attr = node.attribute("type"); std::string shadertype = type_attr ? type_attr.value() : "surface"; @@ -426,7 +458,11 @@ SimpleRaytracer::parse_scene_xml(const std::string& scenefile) } } shadingsys->ShaderGroupEnd(*group); - shaders().push_back(group); + if (name_attr) + shadermap.emplace(name_attr.value(), int(shaders().size())); + shaders().emplace_back(group); + m_shader_is_light.emplace_back( + is_light_attr ? strtobool(is_light_attr.value()) : false); } else { // unknown element? } @@ -436,6 +472,8 @@ SimpleRaytracer::parse_scene_xml(const std::string& scenefile) "Error reading {}: Found multiple top-level elements", scenefile); if (shaders().empty()) errhandler().severefmt("No shaders in scene"); + if (scene.num_prims() == 0) + errhandler().severefmt("No primitives in scene"); camera.finalize(); } @@ -811,29 +849,32 @@ SimpleRaytracer::get_camera_screen_window(ShaderGlobals* /*sg*/, bool derivs, void SimpleRaytracer::globals_from_hit(ShaderGlobals& sg, const Ray& r, - const Dual2& t, int id) + const Dual2& t, int id, float u, + float v) { memset((char*)&sg, 0, sizeof(ShaderGlobals)); Dual2 P = r.point(t); // We are missing the projection onto the surface here - sg.P = P.val(); - sg.dPdx = P.dx(); - sg.dPdy = P.dy(); - Dual2 N = scene.normal(P, id); - sg.Ng = sg.N = N.val(); - Dual2 uv = scene.uv(P, N, sg.dPdu, sg.dPdv, id); - sg.u = uv.val().x; - sg.dudx = uv.dx().x; - sg.dudy = uv.dy().x; - sg.v = uv.val().y; - sg.dvdx = uv.dx().y; - sg.dvdy = uv.dy().y; - sg.surfacearea = scene.surfacearea(id); + sg.P = P.val(); + sg.dPdx = P.dx(); + sg.dPdy = P.dy(); + sg.N = scene.normal(P, sg.Ng, id, u, v); + Dual2 uv = scene.uv(P, sg.N, sg.dPdu, sg.dPdv, id, u, v); + sg.u = uv.val().x; + sg.dudx = uv.dx().x; + sg.dudy = uv.dy().x; + sg.v = uv.val().y; + sg.dvdx = uv.dx().y; + sg.dvdy = uv.dy().y; + const int meshid = std::upper_bound(scene.last_index.begin(), + scene.last_index.end(), id) + - scene.last_index.begin(); + sg.surfacearea = m_mesh_surfacearea[meshid]; Dual2 direction = r.dual_direction(); sg.I = direction.val(); sg.dIdx = direction.dx(); sg.dIdy = direction.dy(); - sg.backfacing = sg.N.dot(sg.I) > 0; + sg.backfacing = sg.Ng.dot(sg.I) > 0; if (sg.backfacing) { sg.N = -sg.N; sg.Ng = -sg.Ng; @@ -865,17 +906,16 @@ Color3 SimpleRaytracer::subpixel_radiance(float x, float y, Sampler& sampler, ShadingContext* ctx) { - Ray r = camera.get(x, y); + constexpr float inf = std::numeric_limits::infinity(); + Ray r = camera.get(x, y); Color3 path_weight(1, 1, 1); Color3 path_radiance(0, 0, 0); int prev_id = -1; - float bsdf_pdf = std::numeric_limits< - float>::infinity(); // camera ray has only one possible direction + float bsdf_pdf = inf; // camera ray has only one possible direction for (int b = 0; b <= max_bounces; b++) { // trace the ray against the scene - Dual2 t; - int id = prev_id; - if (!scene.intersect(r, t, id)) { + Intersection hit = scene.intersect(r, inf, prev_id); + if (hit.t == inf) { // we hit nothing? check background shader if (backgroundShaderID >= 0) { if (b > 0 && backgroundResolution > 0) { @@ -896,9 +936,26 @@ SimpleRaytracer::subpixel_radiance(float x, float y, Sampler& sampler, // construct a shader globals for the hit point ShaderGlobals sg; - globals_from_hit(sg, r, t, id); - const float radius = r.radius + r.spread * t.val(); - int shaderID = scene.shaderid(id); + globals_from_hit(sg, r, hit.t, hit.id, hit.u, hit.v); + if (show_globals) { + // visualize the main fields of the shader globals + Vec3 v = sg.Ng; + if (show_globals == 2) + v = sg.N; + if (show_globals == 3) + v = sg.dPdu.normalize(); + if (show_globals == 4) + v = sg.dPdv.normalize(); + if (show_globals == 5) + v = Vec3(sg.u, sg.v, 0); + Color3 c(v.x, v.y, v.z); + if (show_globals != 5) + c = c * 0.5f + Color3(0.5f); + path_radiance += path_weight * c; + break; + } + const float radius = r.radius + r.spread * hit.t; + int shaderID = scene.shaderid(hit.id); if (shaderID < 0 || !m_shaders[shaderID]) break; // no shader attached? done @@ -910,9 +967,11 @@ SimpleRaytracer::subpixel_radiance(float x, float y, Sampler& sampler, // add self-emission float k = 1; - if (scene.islight(id)) { + if (m_shader_is_light[shaderID]) { + const float light_pick_pdf = 1.0f / m_lightprims.size(); // figure out the probability of reaching this point - float light_pdf = scene.shapepdf(id, r.origin, sg.P); + float light_pdf = light_pick_pdf + * scene.shapepdf(hit.id, r.origin, sg.P); k = MIS::power_heuristic(bsdf_pdf, light_pdf); } path_radiance += path_weight * k * result.Le; @@ -949,53 +1008,57 @@ SimpleRaytracer::subpixel_radiance(float x, float y, Sampler& sampler, * MIS::power_heuristic(bg_pdf, b.pdf); if ((contrib.x + contrib.y + contrib.z) > 0) { - int shadow_id = id; - Ray shadow_ray = Ray(sg.P, bg_dir.val(), radius, 0, - Ray::SHADOW); - Dual2 shadow_dist; - if (!scene.intersect(shadow_ray, shadow_dist, - shadow_id)) // ray reached the background? + Ray shadow_ray = Ray(sg.P, bg_dir.val(), radius, 0, + Ray::SHADOW); + Intersection shadow_hit = scene.intersect(shadow_ray, inf, + hit.id); + if (shadow_hit.t == inf) // ray reached the background? path_radiance += contrib; } } - // trace one ray to each light - for (int lid = 0; lid < scene.num_prims(); lid++) { - if (lid == id) - continue; // skip self - if (!scene.islight(lid)) - continue; // doesn't want to be sampled as a light - int shaderID = scene.shaderid(lid); - if (shaderID < 0 || !m_shaders[shaderID]) - continue; // no shader attached to this light - // sample a random direction towards the object - float light_pdf; - Vec3 ldir = scene.sample(lid, sg.P, xi, yi, light_pdf); - BSDF::Sample b = result.bsdf.eval(-sg.I, ldir); - Color3 contrib = path_weight * b.weight - * MIS::power_heuristic(light_pdf, - b.pdf); - if ((contrib.x + contrib.y + contrib.z) > 0) { - Ray shadow_ray = Ray(sg.P, ldir, radius, 0, Ray::SHADOW); - // trace a shadow ray and see if we actually hit the target - // in this tiny renderer, tracing a ray is probably cheaper than evaluating the light shader - int shadow_id = id; // ignore self hit - Dual2 shadow_dist; - if (scene.intersect(shadow_ray, shadow_dist, shadow_id) - && shadow_id == lid) { - // setup a shader global for the point on the light - ShaderGlobals light_sg; - globals_from_hit(light_sg, shadow_ray, shadow_dist, lid); - // execute the light shader (for emissive closures only) - shadingsys->execute(*ctx, *m_shaders[shaderID], light_sg); - ShadingResult light_result; - process_closure(light_sg, light_result, light_sg.Ci, true); - // accumulate contribution - path_radiance += contrib * light_result.Le; + // trace a shadow ray to one of the light emitting primitives + if (!m_lightprims.empty()) { + const float light_pick_pdf = 1.0f / m_lightprims.size(); + + // uniform probability for each light + float xl = xi * m_lightprims.size(); + int ls = floorf(xl); + xl -= ls; + + uint32_t lid = m_lightprims[ls]; + if (lid != hit.id) { + int shaderID = scene.shaderid(lid); + // sample a random direction towards the object + LightSample sample = scene.sample(lid, sg.P, xl, yi); + BSDF::Sample b = result.bsdf.eval(-sg.I, sample.dir); + Color3 contrib = path_weight * b.weight + * MIS::power_heuristic( + light_pick_pdf * sample.pdf, b.pdf); + if ((contrib.x + contrib.y + contrib.z) > 0) { + Ray shadow_ray = Ray(sg.P, sample.dir, radius, 0, + Ray::SHADOW); + // trace a shadow ray and see if we actually hit the target + // in this tiny renderer, tracing a ray is probably cheaper than evaluating the light shader + Intersection shadow_hit + = scene.intersect(shadow_ray, sample.dist, hit.id, lid); + if (shadow_hit.t == sample.dist) { + // setup a shader global for the point on the light + ShaderGlobals light_sg; + globals_from_hit(light_sg, shadow_ray, sample.dist, lid, + sample.u, sample.v); + // execute the light shader (for emissive closures only) + shadingsys->execute(*ctx, *m_shaders[shaderID], + light_sg); + ShadingResult light_result; + process_closure(light_sg, light_result, light_sg.Ci, + true); + // accumulate contribution + path_radiance += contrib * light_result.Le; + } } } } - // trace indirect ray and continue BSDF::Sample p = result.bsdf.sample(-sg.I, xi, yi, zi); path_weight *= p.weight; @@ -1008,7 +1071,7 @@ SimpleRaytracer::subpixel_radiance(float x, float y, Sampler& sampler, if (!(path_weight.x > 0) && !(path_weight.y > 0) && !(path_weight.z > 0)) break; // filter out all 0's or NaNs - prev_id = id; + prev_id = hit.id; r.origin = sg.P; } return path_radiance; @@ -1045,6 +1108,7 @@ SimpleRaytracer::prepare_render() max_bounces = options.get_int("max_bounces"); rr_depth = options.get_int("rr_depth"); show_albedo_scale = options.get_float("show_albedo_scale"); + show_globals = options.get_int("show_globals"); // prepare background importance table (if requested) if (backgroundResolution > 0 && backgroundShaderID >= 0) { @@ -1065,6 +1129,43 @@ SimpleRaytracer::prepare_render() // we aren't directly evaluating the background backgroundResolution = 0; } + + // build bvh and prepare triangles + scene.prepare(errhandler()); + + m_mesh_surfacearea.reserve(scene.last_index.size()); + + // measure the total surface area of each mesh + int first_index = 0; + for (int last_index : scene.last_index) { + float area = 0; + for (int index = first_index; index < last_index; index++) { + area += scene.primitivearea(index); + } + m_mesh_surfacearea.emplace_back(area); + first_index = last_index; + } + // collect all light emitting triangles + for (unsigned t = 0, n = scene.num_prims(); t < n; t++) { + int shaderID = scene.shaderid(t); + if (shaderID < 0 || !m_shaders[shaderID]) + continue; // no shader attached + if (m_shader_is_light[shaderID]) + m_lightprims.emplace_back(t); + } + if (!m_lightprims.empty()) + errhandler().infofmt("Found {} triangles to be treated as lights", + m_lightprims.size()); +#if 0 + // dump scene to disk as obj for debugging purposes + // TODO: make this a feature? + FILE* fp = fopen("/tmp/test.obj", "w"); + for (Vec3 v : scene.verts) + fprintf(fp, "v %.9g %.9g %.9g\n", v.x, v.y, v.z); + for (TriangleIndices t : scene.triangles) + fprintf(fp, "f %d %d %d\n", 1 + t.a, 1 + t.b, 1 + t.c); + fclose(fp); +#endif } @@ -1072,6 +1173,7 @@ SimpleRaytracer::prepare_render() void SimpleRaytracer::render(int xres, int yres) { + OIIO::Timer timer; ShadingSystem* shadingsys = this->shadingsys; OIIO::parallel_for_chunked( 0, yres, 0, [&, this](int64_t ybegin, int64_t yend) { @@ -1097,6 +1199,10 @@ SimpleRaytracer::render(int xres, int yres) shadingsys->release_context(ctx); shadingsys->destroy_thread_info(thread_info); }); + double rendertime = timer(); + errhandler().infofmt("Rendered {}x{} image with {} samples in {}", xres, + yres, aa * aa, + OIIO::Strutil::timeintervalformat(rendertime, 2)); } diff --git a/src/testrender/simpleraytracer.h b/src/testrender/simpleraytracer.h index e9cae486b..b873bf56b 100644 --- a/src/testrender/simpleraytracer.h +++ b/src/testrender/simpleraytracer.h @@ -113,7 +113,13 @@ class SimpleRaytracer : public RendererServices { int max_bounces = 1000000; int rr_depth = 5; float show_albedo_scale = 0.0f; + int show_globals = 0; std::vector m_shaders; + std::vector m_shader_is_light; + std::vector + m_mesh_surfacearea; // surface area of all triangles in each mesh (one entry per mesh) + std::vector + m_lightprims; // array of all triangles that have a "light" shader on them class ErrorHandler; // subclass ErrorHandler for SimpleRaytracer std::unique_ptr m_errhandler; @@ -170,7 +176,7 @@ class SimpleRaytracer : public RendererServices { // CPU renderer helpers void globals_from_hit(ShaderGlobals& sg, const Ray& r, - const Dual2& t, int id); + const Dual2& t, int id, float u, float v); Vec3 eval_background(const Dual2& dir, ShadingContext* ctx, int bounce = -1); Color3 subpixel_radiance(float x, float y, Sampler& sampler, diff --git a/src/testrender/testrender.cpp b/src/testrender/testrender.cpp index aa807953e..3143d092b 100644 --- a/src/testrender/testrender.cpp +++ b/src/testrender/testrender.cpp @@ -51,6 +51,7 @@ static std::string texoptions; static int xres = 640, yres = 480; static int aa = 1, max_bounces = 1000000, rr_depth = 5; static float show_albedo_scale = 0.0f; +static int show_globals = 0; static int num_threads = 0; static int iters = 1; static std::string scenefile, imagefile; @@ -174,6 +175,21 @@ getargs(int argc, const char* argv[]) .help("Trace NxN rays per pixel"); ap.arg("-albedo %f:SCALE", &show_albedo_scale) .help("Visualize the albedo of each pixel instead of path tracing"); + ap.arg("-normals") + .help("Visualize the shading normals instead of path tracing") + .action([&](cspan argv) { show_globals = 1; }); + ap.arg("-geonormals") + .help("Visualize the geometric normals instead of path tracing") + .action([&](cspan argv) { show_globals = 2; }); + ap.arg("-utangents") + .help("Visualize the surface tangent with respect to the u texture coordinate instead of path tracing") + .action([&](cspan argv) { show_globals = 3; }); + ap.arg("-vtangents") + .help("Visualize the surface tangent with respect to the v texture coordinate instead of path tracing") + .action([&](cspan argv) { show_globals = 4; }); + ap.arg("-uvs") + .help("Visualize the texture coordinates instead of path tracing") + .action([&](cspan argv) { show_globals = 5; }); ap.arg("--iters %d:N", &iters) .help("Number of iterations"); ap.arg("-O0", &O0) @@ -281,6 +297,7 @@ main(int argc, const char* argv[]) rend->attribute("rr_depth", rr_depth); rend->attribute("aa", aa); rend->attribute("show_albedo_scale", show_albedo_scale); + rend->attribute("show_globals", show_globals); OIIO::attribute("threads", num_threads); #if OSL_USE_OPTIX diff --git a/testsuite/render-background/NOOPTIMIZE b/testsuite/render-background/NOOPTIMIZE deleted file mode 100644 index 5036d37ee..000000000 --- a/testsuite/render-background/NOOPTIMIZE +++ /dev/null @@ -1 +0,0 @@ -Render too expensive without optimization diff --git a/testsuite/render-background/ref/out-macos-alt.exr b/testsuite/render-background/ref/out-macos-alt.exr new file mode 100644 index 000000000..ba80f0cc7 Binary files /dev/null and b/testsuite/render-background/ref/out-macos-alt.exr differ diff --git a/testsuite/render-background/ref/out.exr b/testsuite/render-background/ref/out.exr index 1ddac131f..ea78b6263 100644 Binary files a/testsuite/render-background/ref/out.exr and b/testsuite/render-background/ref/out.exr differ diff --git a/testsuite/render-bumptest/NOOPTIMIZE b/testsuite/render-bumptest/NOOPTIMIZE deleted file mode 100644 index 5036d37ee..000000000 --- a/testsuite/render-bumptest/NOOPTIMIZE +++ /dev/null @@ -1 +0,0 @@ -Render too expensive without optimization diff --git a/testsuite/render-bumptest/ref/out.exr b/testsuite/render-bumptest/ref/out.exr index 0d4c53664..c8839ccad 100644 Binary files a/testsuite/render-bumptest/ref/out.exr and b/testsuite/render-bumptest/ref/out.exr differ diff --git a/testsuite/render-bunny/bunny.obj b/testsuite/render-bunny/bunny.obj new file mode 100644 index 000000000..8fafc11ce --- /dev/null +++ b/testsuite/render-bunny/bunny.obj @@ -0,0 +1,7520 @@ +#### +# +# OBJ File Generated by Meshlab +# +#### +# Object bunny-simplified.obj +# +# Vertices: 2504 +# Faces: 5000 +# +#### +v -0.6549226 1.5432624 -0.4341221 +v -0.6449501 1.4574369 -0.2820443 +v -0.6239652 1.4187540 -0.2845368 +v -0.6258805 1.4337847 -0.3445391 +v -0.6255232 1.4514854 -0.4131335 +v -0.6375787 1.5015883 -0.5412380 +v -0.6217448 1.5268865 -0.4867114 +v -0.6146076 1.4987631 -0.5398766 +v -0.6415928 1.5835307 -0.5055905 +v -0.7243964 1.5932295 -0.4799239 +v -0.6870194 1.6042342 -0.4798095 +v -0.6220906 1.4421134 -0.5233276 +v -0.6433285 1.3799640 -0.4860101 +v -0.6612523 1.3427862 -0.4471364 +v -0.6410622 1.2669116 -0.1991668 +v -0.6403712 1.2600542 -0.1781954 +v -0.6229875 1.2780092 -0.1311536 +v -0.6403540 1.3972768 -0.1074609 +v -0.6208369 1.3840486 -0.1496138 +v -0.6167845 1.3080577 -0.1642132 +v -0.6239814 1.2889034 -0.1857160 +v -0.6120850 1.3516699 -0.1455592 +v -0.6241803 1.3668518 -0.0978642 +v -0.6126696 1.3142434 -0.1244809 +v -0.6759124 1.5927546 -0.5191607 +v -0.7416708 1.4730684 -0.3674590 +v -0.7327747 1.2430303 -0.1640534 +v -0.7133107 1.2305630 -0.1538872 +v -0.7300445 1.2269659 -0.0998875 +v -0.7196568 1.2101648 -0.0582892 +v -0.6203460 1.1710619 0.0364498 +v -0.5882443 1.1610376 0.0462472 +v -0.5395312 1.1739705 0.0824405 +v -0.5002983 1.1263044 0.1065183 +v -0.4865653 1.0863378 0.0985187 +v -0.5681290 1.0908166 0.0402788 +v -0.6016994 1.0832276 0.0248149 +v -0.7721394 0.9717191 0.0280344 +v -0.8857121 1.1049340 0.1434364 +v -0.8942287 1.1839880 0.2506097 +v -0.9131081 1.2377073 0.3642165 +v -0.6766773 1.3065704 0.4279515 +v -0.5904699 1.3088138 0.3906914 +v -0.6668561 1.3260649 0.3463427 +v -0.7520997 1.0572975 0.0227425 +v -0.7903469 1.0824153 0.0492053 +v -0.8280391 1.1532265 0.1071534 +v -0.8662097 1.0819905 0.1106489 +v -0.8406421 1.0477210 0.0664705 +v -0.8641961 1.1406280 0.1301452 +v -0.8082186 1.0336175 0.0475132 +v -0.8351949 1.2953379 0.1900719 +v -0.8801600 1.2858076 0.2420707 +v -0.9147738 1.2614636 0.2850479 +v -0.8989779 1.2382985 0.2247019 +v -0.9139819 1.2124480 0.2765427 +v -0.7231846 1.0832416 0.0206980 +v -0.6940288 1.1151869 0.0181721 +v -0.6573050 1.1231209 0.0201700 +v -0.6330014 1.0964082 0.0223314 +v -0.6469703 1.1360743 0.0236354 +v -0.6261408 1.1164488 0.0259004 +v -0.6169033 1.1350701 0.0297564 +v -0.6808592 1.0722671 0.0121575 +v -0.6376846 1.2369401 -0.0976741 +v -0.6535636 1.3550122 -0.0078173 +v -0.6909062 1.4087873 -0.0433454 +v -0.6436019 1.4199573 -0.1555018 +v -0.6248762 1.4137150 -0.2283412 +v -0.6605703 1.4496909 -0.1845886 +v -0.6910151 1.2060102 -0.0627854 +v -0.6661569 1.2125764 -0.0767984 +v -0.6429232 1.2220535 -0.0729507 +v -0.6419066 1.2119330 -0.0497118 +v -0.6199675 1.2678028 -0.0647667 +v -0.6207663 1.3239708 -0.0464334 +v -0.6167170 1.2948046 -0.0560444 +v -0.6183757 1.2845778 -0.0936950 +v -0.6262469 1.2525102 -0.0826390 +v -0.6426711 1.3686092 -0.0569626 +v -0.6192380 1.5449867 -0.5369086 +v -0.6673485 1.5393387 -0.5243628 +v -0.6431357 1.4660231 -0.5323711 +v -0.6399451 1.4495537 -0.5292225 +v -0.6681446 1.4350611 -0.5133522 +v -0.6416087 1.4377197 -0.5258226 +v -0.6264208 1.4189807 -0.4893627 +v -0.6351672 1.4096297 -0.5109947 +v -0.6856140 1.4120940 -0.4873470 +v -0.6654103 1.3973166 -0.5042708 +v -0.7198250 1.4741944 -0.4108990 +v -0.6982340 1.4852097 -0.4592101 +v -0.6787043 1.5082182 -0.5027832 +v -0.6132728 1.4734076 -0.5292192 +v -0.6585413 1.5703669 -0.5287185 +v -0.6725294 1.4710634 -0.5078398 +v -0.6205308 1.4509486 -0.4774414 +v -0.6425475 1.3733395 -0.4491536 +v -0.6711789 1.3620675 -0.4768901 +v -0.7210497 1.3075333 -0.3913880 +v -0.6986325 1.2869525 -0.3680000 +v -0.6880513 1.2767839 -0.3356027 +v -0.7241387 1.2539304 -0.2720966 +v -0.6914730 1.2525222 -0.2478814 +v -0.7024183 1.2400784 -0.1968812 +v -0.6630213 1.2299325 -0.1346028 +v -0.6926711 1.2178732 -0.1138580 +v -0.6658112 1.2414539 -0.1808168 +v -0.7360646 1.3580807 -0.2624755 +v -0.7273783 1.4336393 -0.3608275 +v -0.7491226 1.3887954 -0.2441059 +v -0.7328960 1.2675513 -0.3110355 +v -0.7259870 1.3207613 -0.3575161 +v -0.7068640 1.4156204 -0.4177066 +v -0.7008823 1.3410197 -0.4458504 +v -0.7119493 1.3550931 -0.4066111 +v -0.7166165 1.3741827 -0.3731903 +v -0.7125643 1.5404129 -0.4684697 +v -0.6936636 1.3703322 -0.4727790 +v -0.6275098 1.4168835 -0.4508129 +v -0.6316978 1.3897558 -0.3854008 +v -0.6481532 1.3538082 -0.4026423 +v -0.6297029 1.4036880 -0.4184666 +v -0.6765997 1.3137324 -0.4184336 +v -0.6621532 1.3166240 -0.3698012 +v -0.6979545 1.3938791 -0.4544098 +v -0.6928014 1.4448453 -0.4611969 +v -0.7463955 1.5659466 -0.4535030 +v -0.7794387 1.5741647 -0.4246839 +v -0.7481639 1.5878695 -0.4164064 +v -0.6987857 1.5763297 -0.4238999 +v -0.6822740 1.5245707 -0.3667832 +v -0.7627838 1.5419954 -0.3383347 +v -0.7212633 1.5471429 -0.3612593 +v -0.7953061 1.5541883 -0.3867486 +v -0.7507274 1.5193321 -0.4049685 +v -0.7882927 1.5393560 -0.4025628 +v -0.8002758 1.4644746 -0.2973992 +v -0.7544390 1.3444588 -0.1418625 +v -0.7811949 1.4464560 -0.1787482 +v -0.7337692 1.4621612 -0.1457997 +v -0.7857149 1.4090662 -0.1414632 +v -0.7805385 1.3587238 -0.1305351 +v -0.7702360 1.4067854 -0.0836612 +v -0.7834165 1.3827217 -0.1685497 +v -0.7769511 1.4017929 -0.2186610 +v -0.8028653 1.4676071 -0.2686061 +v -0.7137643 1.4857100 -0.2011079 +v -0.8003982 1.4286129 -0.2304028 +v -0.8043488 1.5073406 -0.3393638 +v -0.6284642 1.4865992 -0.4265625 +v -0.6226102 1.3934121 -0.3181029 +v -0.6324785 1.3521560 -0.3190143 +v -0.6281362 1.3500521 -0.2980589 +v -0.6564491 1.2810392 -0.2690705 +v -0.6190988 1.3768748 -0.2792225 +v -0.6265395 1.4034610 -0.3607632 +v -0.6375048 1.3624877 -0.3657983 +v -0.6409314 1.3431214 -0.3401776 +v -0.6604167 1.3065256 -0.3350345 +v -0.7298680 1.5217580 -0.3008506 +v -0.6752477 1.2772866 -0.3023000 +v -0.6610230 1.2573776 -0.2237112 +v -0.6487160 1.4480809 -0.2212674 +v -0.6500204 1.4954937 -0.3752047 +v -0.6576187 1.4862171 -0.3294916 +v -0.6972147 1.5068232 -0.2973504 +v -0.6852184 1.4847729 -0.2324845 +v -0.7726790 1.4835402 -0.3468750 +v -0.7601659 1.4388062 -0.2972861 +v -0.7453007 1.3472046 -0.1852688 +v -0.7462932 1.3071363 -0.1772723 +v -0.7602790 1.2875981 -0.1167455 +v -0.7625485 1.2613720 -0.0894138 +v -0.7672589 1.3135053 0.0501932 +v -0.7733628 1.3479328 -0.0134346 +v -0.7282842 1.3353643 -0.3088290 +v -0.7326987 1.4030432 -0.3159061 +v -0.7393119 1.2856815 -0.3192419 +v -0.6682438 1.4839646 -0.2862546 +v -0.6486919 1.3074279 -0.3025465 +v -0.6127750 1.3421719 -0.1861930 +v -0.6145917 1.3694977 -0.2434804 +v -0.6165357 1.3851075 -0.1896596 +v -0.6256012 1.3346943 -0.2708536 +v -0.6185762 1.3258406 -0.2268042 +v -0.7479574 1.2640455 -0.2590553 +v -0.7776233 1.4859624 -0.2459796 +v -0.7535974 1.5047934 -0.2665913 +v -0.6848900 1.4607376 -0.1653513 +v -0.6338313 1.3085864 -0.2596714 +v -0.6357101 1.2879897 -0.2294724 +v -0.7382697 1.3171564 -0.2359017 +v -0.7557132 1.2744732 -0.2156940 +v -0.7283188 1.2484601 -0.2276302 +v -0.1923051 0.5446548 -0.3341234 +v -0.3152038 0.5931352 -0.2287678 +v -0.4247032 0.3408506 -0.0536668 +v -0.4218478 0.1927602 -0.0404459 +v -0.4040371 0.1380571 -0.1998701 +v -0.1294647 0.0314335 -0.1793587 +v -0.0833441 0.0309898 -0.1606452 +v -0.0078549 0.0333540 -0.1115706 +v 0.1196790 0.0305971 -0.1448004 +v 0.0484618 0.2506827 -0.2556622 +v 0.0029675 0.2398224 -0.2577714 +v 0.0443599 0.2100888 -0.2447680 +v -0.0056613 0.1932143 -0.2569905 +v -0.0569175 0.1550970 -0.2393344 +v -0.1595326 0.1623327 -0.2353865 +v -0.1479890 0.1352188 -0.2090359 +v -0.3618565 0.0829924 -0.2435647 +v -0.3819411 0.1284643 -0.2264598 +v -0.3214483 0.2202373 -0.0749050 +v -0.3361970 0.3019061 -0.0612436 +v -0.3114634 0.4138128 -0.2341447 +v -0.2854793 0.6194780 -0.2649047 +v -0.3345053 0.5408329 -0.2329874 +v -0.3352101 0.1996686 -0.1136861 +v -0.4039969 0.1657933 -0.1614750 +v -0.3793212 0.2333931 -0.0284123 +v -0.3654271 0.2779567 -0.0325328 +v -0.4034820 0.3034777 -0.0383830 +v -0.3540834 0.4092932 -0.1028981 +v -0.3380663 0.4391291 -0.1837435 +v -0.3458098 0.4828377 -0.2026570 +v -0.4364321 0.3978186 -0.0844963 +v -0.4653075 0.3576829 -0.0609136 +v -0.3717112 0.3294717 -0.0527033 +v -0.3922406 0.3881778 -0.0749130 +v -0.3958448 0.4445236 -0.0983734 +v -0.3643895 0.4525189 -0.1141506 +v -0.3381770 0.2686768 -0.0391306 +v -0.2065707 0.2021066 -0.2420264 +v -0.1414988 0.3287309 -0.3117055 +v -0.1606817 0.4492332 -0.3337050 +v -0.0145076 0.6348544 -0.2952330 +v -0.2731404 0.5200564 -0.3117766 +v -0.2725564 0.5923965 -0.2992951 +v -0.2065795 0.6047590 -0.3158533 +v -0.2853485 0.4212939 -0.2818443 +v -0.2980578 0.5566686 -0.2905966 +v -0.2394999 0.5885788 -0.3154074 +v -0.3193912 0.4852731 -0.2690620 +v -0.2239330 0.5201187 -0.3313006 +v -0.0734231 0.6288048 -0.3053502 +v -0.0210356 0.7116596 -0.2068120 +v 0.0674588 0.6661041 -0.2564012 +v -0.0284325 0.6768885 -0.2714355 +v 0.0194768 0.6639574 -0.2697132 +v -0.1632955 0.5078101 -0.3306906 +v -0.0248333 0.3486957 -0.2954375 +v -0.1915045 0.2867361 -0.2884232 +v -0.1345237 0.2757509 -0.2896354 +v -0.0991600 0.5742567 -0.3275762 +v -0.1036030 0.3737576 -0.3040115 +v -0.0694461 0.3244464 -0.3015642 +v -0.1121912 0.3125106 -0.2961986 +v -0.1006859 0.2122892 -0.2662778 +v -0.0584663 0.1983462 -0.2566551 +v 0.1129877 0.0266708 -0.0973341 +v 0.1788858 0.0305099 -0.0496447 +v 0.4697852 0.0876683 0.0890429 +v 0.3875457 0.0525795 0.0895259 +v 0.2330463 0.0230322 0.1043620 +v 0.2544302 0.0299332 0.0681801 +v 0.2514378 0.0705324 0.0080363 +v 0.2184957 0.0350363 0.0371835 +v 0.2013459 0.0229315 0.0761237 +v 0.1720041 0.0359703 0.1025450 +v 0.2094104 0.0352758 0.1447534 +v 0.2018508 0.0501742 -0.0576665 +v 0.2185233 0.0587966 -0.0145354 +v 0.1806318 0.0747445 -0.1491636 +v 0.2024498 0.0799575 -0.1246222 +v 0.2378355 0.1152541 -0.0716104 +v 0.2847567 0.1434704 -0.0678500 +v 0.3489356 0.3606336 -0.1343021 +v 0.2727500 0.2809897 -0.1887606 +v 0.1066781 0.3880222 -0.2613603 +v 0.1517460 0.4023955 -0.2651078 +v 0.3885681 0.7229289 -0.0645528 +v 0.4596314 0.6380554 0.0015146 +v 0.4869632 0.6338678 0.0609463 +v 0.4477214 0.7819765 0.2239134 +v 0.3865958 0.8756836 0.1972493 +v 0.3454851 0.8518833 -0.0108204 +v 0.3878033 0.8243132 0.0209030 +v 0.0603324 0.7708257 -0.1532437 +v 0.0360992 0.7134271 -0.1760844 +v 0.0219071 0.6955410 -0.2265959 +v 0.0922998 0.7155856 -0.1609300 +v 0.1618591 0.7714269 -0.1354821 +v 0.2331077 0.9548818 0.0191471 +v 0.1794295 0.6336216 -0.2097814 +v 0.1332802 0.6735286 -0.2083851 +v 0.0969099 0.6631299 -0.2394021 +v 0.0727399 0.6912929 -0.2122449 +v 0.1147940 0.7920866 -0.1404008 +v 0.1257086 0.7538115 -0.1466677 +v 0.0482932 0.7333487 -0.1625086 +v 0.2674739 0.8761770 -0.0543985 +v 0.2993931 0.8790058 -0.0153310 +v 0.2795014 0.9250787 0.0142720 +v 0.2055147 0.9039320 -0.0656165 +v 0.2169993 0.8529703 -0.0918685 +v 0.2940634 0.8356453 -0.0579907 +v 0.3228776 0.7940446 -0.0714395 +v 0.3792462 0.7931306 -0.0230600 +v 0.4328545 0.8060051 0.1449065 +v 0.4166834 0.8317129 0.1937674 +v 0.3506902 0.8250945 -0.0316761 +v 0.2361168 0.9082505 -0.0413315 +v 0.3002186 0.9390951 0.0788662 +v 0.2363519 0.9664180 0.3583919 +v 0.0539393 1.0476364 0.2527710 +v 0.0247418 1.0470018 0.1785596 +v 0.2808813 0.9695817 0.3070127 +v 0.2142396 1.0066359 0.2806489 +v 0.1817820 1.0039232 0.0623585 +v 0.1838096 0.9820096 0.0277219 +v 0.1901539 0.9545419 -0.0163569 +v 0.1655392 0.9171885 -0.0755396 +v 0.0887963 1.0420940 0.1117486 +v 0.1498056 1.0330118 0.1149143 +v 0.0655514 1.0357840 0.0808965 +v 0.1298217 1.0114368 0.0409398 +v -0.0012026 1.0387880 0.1100974 +v 0.0820012 0.9243439 -0.0842460 +v 0.1204852 0.9540886 -0.0506166 +v 0.1269565 0.9277390 -0.0782165 +v 0.0348612 0.8600888 -0.1353231 +v 0.0298038 0.8236231 -0.1380848 +v -0.0054358 0.8383324 -0.1435713 +v -0.0124176 0.7895475 -0.1598175 +v -0.0468849 0.8214313 -0.1604334 +v -0.0688970 0.7828711 -0.1634268 +v -0.1363048 0.7934718 -0.1515776 +v -0.0401031 0.9425060 -0.0539581 +v 0.0369583 0.9273123 -0.0727251 +v 0.0341215 0.8982073 -0.1095506 +v 0.0361960 0.9756057 -0.0155895 +v -0.0668131 1.0120476 0.0474309 +v -0.0500899 0.9937655 -0.0043255 +v 0.2097235 0.9384941 0.4127415 +v 0.2299323 0.8972002 0.4424771 +v 0.1180884 0.8039071 0.5043918 +v 0.1452781 0.7878658 0.5013348 +v 0.0123356 0.6748384 0.6401289 +v -0.3279569 0.5858588 0.5351656 +v -0.4335259 0.6700440 0.5440793 +v -0.4690346 0.6382344 0.5540271 +v -0.4679630 0.5801289 0.5715567 +v -0.5319045 0.4241479 0.5052567 +v -0.6503775 0.6268752 0.5653012 +v -0.8507693 0.5395571 0.3939616 +v -0.8345829 0.4963118 0.3487948 +v -0.8762885 0.6907178 0.0879046 +v -0.7828543 0.8760909 0.0600478 +v -0.6369214 0.9212324 -0.0067823 +v -0.4209403 0.8168732 -0.1193795 +v -0.3686985 0.8207110 -0.1218805 +v -0.3503703 0.7587245 -0.1416719 +v -0.3195807 0.7141643 -0.1558185 +v -0.8919839 0.5793152 0.1414247 +v -0.9035350 0.6182575 0.1444445 +v -0.8592257 0.8158913 0.3385338 +v -0.9393377 0.8971051 0.3018595 +v -0.9345051 1.0605201 0.2411274 +v -0.9126795 1.0909141 0.1834866 +v -0.9027226 1.1247090 0.2503818 +v -0.9105443 1.1105413 0.3218212 +v -0.9312730 1.0554471 0.3839255 +v -0.9536142 0.9683379 0.3569955 +v -0.8874865 0.7789251 0.3112468 +v -0.9013895 0.6019524 0.2992850 +v -0.9138921 0.6366435 0.2104255 +v -0.8900546 0.5834944 0.2503047 +v -0.8693840 0.5085242 0.2785163 +v -0.9029274 0.5969062 0.2186500 +v -0.8817673 0.5604582 0.1651480 +v -0.8728145 0.5124208 0.1935828 +v -0.8679262 0.5224973 0.1439541 +v -0.8422898 0.4967856 0.1026090 +v -0.8521455 0.5386153 0.0958652 +v -0.8377507 0.8314331 0.3815060 +v -0.8170110 0.8386450 0.4267097 +v -0.8073168 0.8489805 0.5776011 +v -0.7949187 0.8894321 0.6080190 +v -0.8572628 0.9968919 0.6179222 +v -0.7427127 1.2867073 0.4672795 +v -0.8347511 1.0860580 0.6220124 +v -0.8979222 1.0756284 0.5223789 +v -0.8611645 0.8459277 0.5020844 +v -0.8989170 0.8678386 0.5185149 +v -0.8739273 0.8635189 0.4435422 +v -0.8189522 0.8359222 0.5233153 +v -0.9090646 0.8822302 0.4590536 +v -0.8416771 0.8467916 0.4590069 +v -0.8604227 0.8640130 0.4093963 +v -0.9289062 0.9610692 0.5513071 +v -0.9326403 0.9197070 0.5170376 +v -0.8843904 0.6736246 0.3812576 +v -0.9052143 0.7365022 0.3141831 +v -0.8729727 0.5615679 0.3265644 +v -0.9128524 0.6879115 0.2645842 +v -0.9088855 0.7346330 0.2151242 +v -0.9042153 0.8684524 0.3146020 +v -0.9301326 0.9867797 0.4256837 +v -0.9182881 1.0335701 0.4574762 +v -0.8898613 1.1479750 0.4955326 +v -0.8720700 1.1480434 0.5382414 +v -0.7134889 1.1600269 0.6052572 +v -0.8373914 1.1674675 0.5601604 +v -0.8994628 1.1569140 0.3640788 +v -0.9212038 1.1684939 0.3075724 +v -0.8773950 1.2067887 0.4628795 +v -0.7356694 1.1924946 0.5856177 +v -0.7443205 1.2365520 0.5406774 +v -0.7918784 1.2366097 0.5220079 +v -0.9136847 1.1981788 0.3492172 +v -0.9110107 1.1988335 0.3962843 +v -0.8872067 1.1204585 0.3992440 +v -0.8960755 1.0917101 0.4422826 +v -0.7815433 1.0967991 0.6482770 +v -0.7971097 1.1356964 0.6151040 +v -0.8690314 0.8502695 0.3304904 +v -0.8803041 1.0295644 0.0956634 +v -0.7281955 0.9358991 0.0174279 +v -0.9028426 0.9441406 0.1329940 +v -0.8338374 0.9781258 0.0612911 +v -0.8415767 0.8885671 0.0948878 +v -0.8953531 0.8927712 0.1585269 +v -0.9013785 0.8701935 0.2030093 +v -0.8981571 0.7777497 0.2162691 +v -0.8386154 0.8182755 0.0872208 +v -0.8691582 0.8020731 0.1423732 +v -0.8241702 0.7945924 0.0514174 +v -0.8832190 0.7534637 0.1518123 +v -0.8570315 0.7484829 0.0669481 +v -0.8256825 0.8593652 0.1023477 +v -0.7964833 0.8327968 0.0440542 +v -0.8076239 0.9125821 0.0603811 +v -0.8510637 0.8447916 0.1379205 +v -0.8615198 0.9378600 0.0903357 +v -0.8849743 0.9792674 0.0926197 +v -0.8708749 0.8965505 0.1265482 +v -0.8054554 0.9614056 0.0471726 +v -0.7037306 1.0241325 0.0091205 +v -0.7136769 0.9720581 0.0117356 +v -0.6711800 0.9427931 0.0078164 +v -0.6209830 1.0068042 0.0094610 +v -0.5935698 1.0363557 0.0226163 +v -0.6510796 1.0384550 0.0095861 +v -0.6570920 0.9847589 0.0068681 +v -0.6033901 0.9537152 0.0013122 +v -0.5499458 0.9712376 0.0033065 +v -0.4737335 1.0038463 0.0134644 +v -0.4405475 1.0246106 0.0611084 +v -0.3643090 1.0234663 0.1334839 +v -0.1703496 0.9546134 0.3943513 +v -0.1283523 0.9632805 0.4048274 +v -0.0309784 0.9611495 0.4422017 +v -0.2726561 0.9944673 0.1533690 +v -0.2205682 0.9658438 0.0200831 +v -0.3457319 0.8829902 -0.0846132 +v -0.3135610 0.8843763 -0.0846902 +v -0.2178738 0.9345851 -0.0349072 +v -0.1875055 0.9880844 0.0648402 +v -0.1440466 0.9924364 0.0548069 +v -0.0890620 1.0316167 0.1801046 +v 0.0659641 1.0246259 0.3485373 +v 0.0698979 0.9728748 0.4231555 +v -0.0677314 1.0293961 0.1124077 +v -0.1142761 1.0154150 0.0977168 +v -0.1035750 0.9930142 0.0257599 +v -0.1338366 0.9655870 0.0090053 +v -0.1762856 0.9650454 0.0040124 +v -0.1376243 0.9417656 -0.0238881 +v -0.3377297 0.8514037 -0.1035566 +v -0.1671535 0.9118028 -0.0656101 +v -0.2332822 0.8898545 -0.0760512 +v -0.1968432 0.8974622 -0.0738641 +v -0.3023851 0.6749812 -0.1705638 +v -0.2749543 0.6522644 -0.2289169 +v -0.3574562 0.9421016 -0.0299897 +v -0.3873208 0.8843047 -0.0828690 +v -0.3105015 0.9252784 -0.0376658 +v -0.3656838 0.9975434 0.0223648 +v -0.4055299 1.0213499 0.0682604 +v -0.2819609 0.9532943 0.0031756 +v -0.5162160 1.0847688 0.0607447 +v -0.5104610 1.0539377 0.0563529 +v -0.4221411 1.0420741 0.2033274 +v -0.2511115 0.9934942 0.2464538 +v -0.2687984 0.9636195 0.3192598 +v -0.4276521 1.0377033 0.1470448 +v -0.4450741 1.0484812 0.1388361 +v -0.4732299 1.0288128 0.0590515 +v -0.3472548 1.0215480 0.2012160 +v -0.4337651 0.9621453 -0.0350020 +v -0.5826355 0.9208729 -0.0368443 +v -0.4060768 0.9998160 0.0082903 +v -0.3841078 0.9666488 -0.0253714 +v -0.4163094 0.9246877 -0.0582965 +v -0.5177869 0.9306257 -0.0513136 +v -0.5203716 0.9639652 -0.0191911 +v -0.4700296 0.9736618 -0.0239104 +v -0.5405114 0.8804373 -0.0781742 +v -0.4780289 0.9259433 -0.0598129 +v -0.4658767 0.8315887 -0.1017266 +v -0.5965476 0.9799153 0.0104863 +v -0.4801281 1.0448227 0.0804019 +v -0.5059949 0.9999759 0.0182557 +v -0.5551714 1.0218604 0.0325705 +v -0.5146810 1.0301797 0.0495530 +v -0.5371677 1.0027596 0.0297717 +v -0.6138733 1.0604583 0.0180592 +v -0.5612766 1.0651168 0.0357604 +v -0.7378697 0.5046904 0.4918401 +v -0.7845615 0.4982403 0.4418868 +v -0.8225956 0.5226099 0.4252199 +v -0.7537861 0.4096182 0.3773772 +v -0.8064652 0.4823669 0.3913239 +v -0.6693311 0.3317867 0.3256027 +v -0.5891609 0.2511225 0.1048484 +v -0.5871330 0.2150898 0.0715628 +v -0.7055250 0.0419032 0.0319614 +v -0.6491529 0.0404506 0.0007964 +v -0.4180835 0.0322397 0.0861123 +v -0.3849485 0.0474943 0.1955174 +v -0.4591269 0.0329087 0.0744741 +v -0.4487290 0.0493732 0.1680497 +v -0.7335524 0.0430979 0.0701462 +v -0.7483591 0.0571834 0.1005668 +v -0.6240471 0.0864882 0.2993356 +v -0.7088391 0.1459255 0.1069577 +v -0.7133182 0.0807181 0.0263977 +v -0.6682233 0.1225183 0.0209926 +v -0.7377821 0.1060165 0.1253049 +v -0.7530521 0.0761015 0.1455395 +v -0.6612417 0.1667938 0.1718342 +v -0.7413684 0.1033857 0.1720114 +v -0.5804161 0.1187647 0.2924353 +v -0.6428092 0.0887848 0.2710652 +v -0.6163861 0.1428358 0.2354296 +v -0.6741898 0.1142933 0.2324226 +v -0.6917069 0.1313932 0.1965852 +v -0.7174760 0.3935474 0.3965311 +v -0.5455432 0.2720399 0.2598316 +v -0.5823954 0.2862060 0.2687405 +v -0.5947554 0.3414235 0.4008434 +v -0.6553993 0.3545210 0.3857250 +v -0.6876773 0.4018819 0.4390347 +v -0.6193290 0.3044629 0.2958379 +v -0.2267456 0.6296522 0.5904053 +v -0.2780705 0.5722232 0.5413778 +v -0.3152544 0.6243494 0.5527922 +v -0.2619413 0.6255262 0.5478773 +v -0.4438615 0.9547097 0.3733331 +v -0.6869846 0.7426197 0.5243122 +v -0.8005385 0.7955993 0.4341509 +v -0.8081515 0.8184820 0.4210052 +v -0.8443894 0.7297803 0.4185923 +v -0.8446833 0.6213333 0.4207428 +v -0.7916259 0.6424653 0.4818225 +v -0.8163133 0.5978123 0.4589054 +v -0.7337694 0.6500474 0.5323175 +v -0.6323504 0.7521992 0.5382459 +v -0.5369492 0.6887623 0.5505883 +v -0.5496008 0.7351757 0.5483139 +v -0.5110893 0.7521856 0.5291140 +v -0.4859547 0.7895188 0.5085859 +v -0.3803570 0.7322956 0.5214924 +v -0.4105200 0.7502601 0.5300209 +v -0.2930014 0.6882965 0.5561131 +v -0.4812182 0.7156572 0.5518374 +v -0.4740688 0.6694644 0.5636041 +v -0.8248811 0.6862826 0.4496562 +v -0.8567390 0.7759829 0.3849139 +v -0.7885312 0.7650816 0.4547116 +v -0.2357541 0.8707870 0.4492107 +v -0.2173202 0.9800543 0.3032785 +v -0.2832972 0.9929064 0.2686646 +v -0.2108684 0.9197194 0.4212706 +v -0.3270898 0.7360663 0.5248557 +v -0.0683869 0.6574356 0.6549017 +v -0.1087497 0.6190110 0.6724274 +v -0.0946235 0.7200916 0.5416131 +v 0.0232930 0.8570033 0.5161189 +v -0.1371686 0.6611751 0.6367960 +v -0.1056687 0.6869068 0.6148407 +v 0.0042730 0.8015299 0.5357184 +v -0.0489358 0.7059516 0.6034165 +v 0.0608101 0.8168783 0.5231669 +v 0.1206408 0.7360750 0.5530561 +v 0.0507391 0.7254475 0.5385234 +v 0.0767100 0.7632126 0.5201212 +v 0.0378554 1.0139689 0.0404910 +v -0.0080499 1.0143272 0.0307172 +v 0.2701290 0.9783443 0.1401822 +v 0.2244130 1.0076541 0.2207554 +v 0.1906065 1.0228422 0.1559977 +v 0.2980438 0.9414893 0.3448161 +v 0.3310638 0.9253289 0.1139126 +v 0.3227017 0.9420536 0.1648645 +v 0.3152553 0.9082794 0.3681254 +v 0.3313043 0.8033257 0.4494616 +v 0.3004683 0.8584358 0.4247680 +v 0.2983011 0.7958727 0.4707259 +v 0.3438775 0.9235389 0.2188302 +v 0.2842348 0.9741318 0.2104638 +v 0.3920672 0.8369653 0.3472013 +v 0.4286441 0.8004625 0.2992692 +v 0.4662080 0.7147452 0.2871922 +v 0.4468456 0.7478329 0.3313864 +v 0.4071735 0.8419025 0.2899712 +v 0.3304774 0.9325706 0.2673256 +v 0.3607400 0.9064709 0.1799585 +v 0.4131020 0.8337676 0.1333772 +v 0.3891985 0.8674646 0.1186818 +v 0.3412499 0.8820202 0.0262480 +v 0.3266826 0.9040049 0.0406056 +v 0.3631391 0.8962476 0.1101756 +v 0.4996065 0.6350635 0.1946151 +v 0.3436316 0.6269689 0.5204818 +v 0.2317854 0.5660602 0.5883303 +v 0.1056113 0.6154470 0.6436104 +v 0.1686491 0.6921562 0.5776813 +v 0.1707199 0.7340463 0.5641581 +v 0.1579186 0.6351568 0.6189057 +v 0.0944749 0.6770453 0.6161503 +v 0.2977870 0.3800555 0.5240293 +v 0.1688729 0.2947469 0.5963688 +v 0.0392421 0.1733472 0.6055288 +v -0.2810182 0.0720718 0.4336799 +v -0.3092345 0.0675377 0.3988904 +v -0.2411317 0.0697177 0.3231821 +v -0.0592502 0.0718706 0.3840710 +v 0.1097533 0.0256026 0.4442564 +v 0.1166515 0.0546853 0.3707471 +v 0.0464095 0.0662616 0.3231091 +v 0.0061808 0.0725817 0.3764943 +v 0.0972734 0.0241947 0.2946479 +v 0.2287287 0.0262128 0.3667336 +v 0.1488747 0.2278494 0.6128294 +v 0.2818151 0.3160022 0.5382720 +v 0.1890868 0.2277136 0.5724351 +v 0.2397073 0.2666831 0.5715348 +v 0.4495022 0.1827360 0.3938588 +v 0.4659013 0.2466162 0.3876487 +v 0.5169735 0.2973418 0.3735592 +v 0.4779586 0.3353501 0.3381250 +v 0.4963837 0.3703186 0.3130791 +v 0.5007418 0.4102854 0.2744659 +v 0.5154772 0.4042012 0.2618618 +v 0.5701742 0.4205832 0.1510967 +v 0.6085600 0.4103928 0.1408761 +v 0.0706075 0.0235774 0.4703164 +v 0.1661986 0.0238331 0.3474153 +v 0.1867316 0.0229033 0.4080708 +v 0.1281497 0.0269038 0.3415426 +v 0.1403381 0.0303711 0.3975548 +v 0.2048259 0.0243456 0.4651899 +v 0.2410671 0.0657447 0.4911137 +v 0.0022115 0.0241290 0.5370182 +v 0.0866099 0.0310988 0.3195061 +v -0.2648072 0.0843590 -0.0836630 +v -0.3332471 0.0837859 -0.0964189 +v -0.4397499 0.1147739 -0.1712697 +v -0.5531353 0.0844607 -0.0388743 +v -0.6611949 0.0661770 -0.0021957 +v -0.4921352 0.1052860 -0.0419938 +v -0.4544874 0.1630245 -0.0540413 +v -0.7960866 0.4528104 0.0513523 +v -0.8315554 0.4623022 0.1299325 +v -0.8054510 0.4516022 0.1048540 +v -0.8468323 0.4726197 0.1570842 +v -0.8216363 0.4726084 0.0688975 +v -0.7721832 0.4599333 0.0177323 +v -0.7654761 0.4874490 -0.0090299 +v -0.7389960 0.5015677 -0.0484474 +v -0.6928197 0.5311676 -0.0875007 +v -0.5523473 0.5590910 -0.1353372 +v -0.5268527 0.5159068 -0.1168725 +v -0.6290367 0.5015497 -0.1001491 +v -0.6026467 0.4197012 -0.0642010 +v -0.6316980 0.4480266 -0.0746283 +v -0.5879803 0.5302900 -0.1234925 +v -0.6046380 0.5792675 -0.1254875 +v -0.6292874 0.5485278 -0.1101729 +v -0.7356580 0.4636057 -0.0201348 +v -0.6687100 0.5614894 -0.1029320 +v -0.8402261 0.7105504 0.0401440 +v -0.7578203 0.5541357 -0.0601673 +v -0.8279642 0.6760483 0.0072069 +v -0.7979200 0.5943813 -0.0285500 +v -0.8044147 0.7731647 0.0173236 +v -0.7154567 0.8705648 0.0007896 +v -0.7439383 0.8956507 0.0320381 +v -0.6897475 0.9052857 0.0073659 +v -0.6103613 0.7884333 -0.0956637 +v -0.5829574 0.7466568 -0.1075421 +v -0.5822306 0.6888623 -0.1298962 +v -0.5452346 0.6585851 -0.1395960 +v -0.4931713 0.4623982 -0.0861074 +v -0.5762374 0.4789413 -0.1036436 +v -0.5412965 0.4616800 -0.0877894 +v -0.5687590 0.5969877 -0.1334346 +v -0.6285579 0.6059099 -0.1089788 +v -0.5782356 0.8169237 -0.0935941 +v -0.5026711 0.8382584 -0.1039914 +v -0.5376627 0.7941386 -0.1076002 +v -0.4708507 0.5926335 -0.1409342 +v -0.3847885 0.5175794 -0.1224545 +v -0.5429907 0.7216702 -0.1382040 +v -0.5032040 0.6985207 -0.1423737 +v -0.4258305 0.5996270 -0.1373275 +v -0.3790678 0.5853261 -0.1504718 +v -0.4409310 0.7185100 -0.1362499 +v -0.5074930 0.6583879 -0.1347208 +v -0.3992794 0.6205570 -0.1576382 +v -0.3932332 0.6787494 -0.1542815 +v -0.4288974 0.6618686 -0.1537918 +v -0.4585333 0.6686028 -0.1413872 +v -0.3829270 0.7782338 -0.1249101 +v -0.4707544 0.6323212 -0.1407329 +v -0.4270894 0.5606704 -0.1336609 +v -0.3845235 0.5523779 -0.1472130 +v -0.3552708 0.5473663 -0.1613006 +v -0.5245479 0.7643879 -0.1276573 +v -0.4795267 0.7899789 -0.1203636 +v -0.4862444 0.7488670 -0.1356743 +v -0.3902433 0.7322437 -0.1421296 +v -0.3584762 0.6900180 -0.1578864 +v -0.5455083 0.8406011 -0.0975691 +v -0.8318363 0.6191267 0.0225740 +v -0.7398974 0.4327698 0.0270464 +v -0.7051710 0.4394736 -0.0179161 +v -0.6680707 0.4487877 -0.0572734 +v -0.6516307 0.3876682 0.0054105 +v -0.6933652 0.4211824 -0.0082076 +v -0.6485909 0.4094683 -0.0324150 +v -0.5829528 0.3208456 0.0465814 +v -0.5912062 0.3015299 0.0900424 +v -0.6126060 0.1488051 0.0204819 +v -0.5912253 0.1838908 0.0522713 +v -0.4650815 0.2385013 -0.0286021 +v -0.5645423 0.3618107 -0.0020188 +v -0.5502994 0.3241610 -0.0075716 +v -0.3703454 0.2023897 -0.0429242 +v -0.4306563 0.2591553 -0.0356798 +v -0.5045903 0.3004969 -0.0318923 +v -0.4716551 0.3100345 -0.0443387 +v -0.5200869 0.4141420 -0.0611025 +v -0.5738360 0.4066841 -0.0509125 +v -0.4789025 0.4086328 -0.0765033 +v -0.5355802 0.3776684 -0.0289393 +v -0.5047060 0.3617142 -0.0454861 +v -0.4758059 0.1886753 -0.0234243 +v -0.3818613 0.0406877 -0.1115724 +v -0.2484592 0.0780547 0.0137167 +v -0.2815801 0.0824729 -0.1123478 +v -0.4390668 0.0390662 -0.2034335 +v 0.0155980 0.0686222 0.3007609 +v 0.0322732 0.0284879 0.2613401 +v 0.0371155 0.0235337 0.1740883 +v 0.1252507 0.0277676 0.2072399 +v 0.2535836 0.0231262 0.2508496 +v 0.2287740 0.0370132 0.2447718 +v 0.2806675 0.0213678 0.2093746 +v 0.2430749 0.0366633 0.1877171 +v 0.1654283 0.0322433 0.2149598 +v 0.1202359 0.0292493 0.1458616 +v 0.1042746 0.0313938 0.1074725 +v 0.1095124 0.0227988 0.0691197 +v 0.1571891 0.0324055 0.1600964 +v 0.1585197 0.0218707 0.0678728 +v 0.2021615 0.0358906 0.2068436 +v 0.0843625 0.0256665 0.1582464 +v 0.0588829 0.0357324 0.3013391 +v 0.0852669 0.0224694 0.2361562 +v 0.1192321 0.0253274 0.2777975 +v 0.1549867 0.0286959 0.2570140 +v 0.1898446 0.0319102 0.2505420 +v 0.2556849 0.0302127 0.3162428 +v 0.3172223 0.0400753 0.2828180 +v 0.4536888 0.1499392 0.3876434 +v 0.5044914 0.1915224 0.3857028 +v 0.5401976 0.1143529 0.2084733 +v 0.3747417 0.0433692 0.2338262 +v 0.3287866 0.0651116 0.3344737 +v 0.2241475 0.0232569 0.2876295 +v 0.4627683 0.1218813 0.3476234 +v 0.4217360 0.1128186 0.3620348 +v 0.5178111 0.1450811 0.3326000 +v 0.4080915 0.0643330 0.2793934 +v 0.4968977 0.0975946 0.2526928 +v 0.5174211 0.2364454 0.3882277 +v 0.5728957 0.3323168 0.3454756 +v 0.5614930 0.4158475 0.2585298 +v 0.6167939 0.3405136 0.0689701 +v 0.5657375 0.3592981 0.0532393 +v 0.5285581 0.2965329 0.0035176 +v 0.4142532 0.2627228 -0.0016087 +v 0.3709097 0.2652786 -0.0748199 +v 0.2732798 0.5596707 -0.1905511 +v 0.2233653 0.4744095 -0.2209596 +v 0.2746777 0.4950702 -0.1986240 +v 0.2938347 0.3925707 -0.1838813 +v 0.2650958 0.4263935 -0.2086616 +v 0.2278556 0.4236679 -0.2273879 +v 0.2282442 0.5227866 -0.2192660 +v 0.2983133 0.3499976 -0.1600391 +v 0.3822004 0.3192014 -0.0832038 +v 0.3531951 0.2353988 -0.0639430 +v 0.3510602 0.1489818 -0.0115348 +v 0.3048081 0.1442938 -0.0200811 +v 0.2413961 0.0957131 -0.0287930 +v 0.5888838 0.1527954 0.1785970 +v 0.6141245 0.1858335 0.1443030 +v 0.6375216 0.3075238 0.0975188 +v 0.6387803 0.3836329 0.1429697 +v 0.6589053 0.3221780 0.1810149 +v 0.6454164 0.3359722 0.1283777 +v 0.6542588 0.2790002 0.1516467 +v 0.6359807 0.2725777 0.0992746 +v 0.6362715 0.2301900 0.1567432 +v 0.6448426 0.2474507 0.2132608 +v 0.5313307 0.2553803 -0.0058020 +v 0.4847163 0.2063399 -0.0163973 +v 0.5100197 0.1631414 0.0147184 +v 0.4002525 0.2162798 -0.0179942 +v 0.4415175 0.2983305 0.0162505 +v 0.4923787 0.2648349 -0.0103317 +v 0.5275826 0.2130772 0.0009685 +v 0.5608884 0.2251252 0.0177108 +v 0.5934382 0.2156997 0.0650055 +v 0.5701575 0.1761251 0.0603552 +v 0.5630066 0.1383872 0.1405775 +v 0.5576031 0.1471788 0.0898236 +v 0.6151883 0.2221531 0.0975944 +v 0.5945735 0.1738545 0.1030226 +v 0.5933714 0.2510915 0.0452496 +v 0.5239287 0.1319403 0.0635072 +v 0.5265771 0.1085829 0.1338403 +v 0.5023850 0.0915472 0.1923413 +v 0.3504142 0.0332276 0.1221065 +v 0.4725384 0.0779556 0.1494282 +v 0.4144640 0.0534041 0.1320778 +v 0.1956760 0.0231878 0.2929825 +v 0.1542607 0.0245796 0.2852310 +v -0.0017281 0.0673685 0.2713423 +v -0.0212218 0.0702074 0.3369426 +v -0.1019289 0.0690914 0.2616484 +v -0.1174006 0.0719492 0.1678590 +v -0.1654965 0.0701420 0.2504919 +v -0.0498151 0.0698312 0.3056035 +v -0.1288096 0.0695987 0.3017690 +v -0.1223849 0.0742993 0.4263673 +v -0.2716590 0.0713920 0.3873690 +v -0.1711040 0.0703386 0.3282672 +v -0.2039291 0.0699000 0.3235215 +v -0.0705856 0.0751974 0.4523938 +v -0.0576294 0.0270706 0.5634781 +v -0.0938787 0.0266266 0.5319870 +v -0.1432129 0.0304899 0.5398652 +v -0.2201413 0.0550151 0.5079039 +v -0.0356382 0.0472793 0.4805536 +v 0.0214672 0.0743088 0.4223649 +v -0.0068671 0.0672793 0.4460417 +v -0.2756882 0.0462303 0.5030897 +v -0.4706244 0.1775310 0.4974511 +v -0.5019692 0.2058783 0.4593192 +v -0.5319316 0.1789082 0.3220735 +v -0.5086435 0.2350407 0.2671258 +v -0.6120524 0.2932210 0.1958434 +v -0.5847479 0.2301963 0.1555649 +v -0.5728967 0.1801572 0.2063587 +v -0.5107006 0.1905419 0.2618799 +v -0.4687640 0.0350830 0.5184454 +v -0.5657530 0.1568822 0.3371723 +v -0.6547609 0.1146800 0.3675196 +v -0.5406563 0.1457072 0.2859363 +v -0.5494955 0.1842256 0.3748082 +v -0.6033283 0.1746179 0.4320357 +v -0.6698313 0.1106708 0.4676366 +v -0.6422111 0.1573255 0.4424529 +v -0.6501651 0.0385271 0.4283526 +v -0.3404108 0.0383526 0.4265794 +v -0.5924585 0.0373246 0.4571700 +v -0.4733511 0.0366487 0.4062684 +v -0.3913093 0.0370871 0.5183213 +v -0.3338391 0.0328383 0.4783828 +v -0.2882107 0.0666209 0.3329264 +v -0.2301548 0.0519255 0.6452927 +v -0.2266403 0.3944563 0.5518097 +v -0.2046595 0.4764467 0.6432022 +v -0.2237411 0.5158385 0.6471156 +v -0.1464693 0.5538898 0.6827269 +v 0.0583599 0.6535150 0.6480227 +v -0.0587488 0.4701543 0.6807787 +v -0.1490080 0.4615864 0.6613709 +v 0.1293170 0.5469386 0.6580611 +v 0.2505120 0.5144491 0.6019583 +v 0.3184156 0.4229515 0.5258110 +v 0.1733132 0.5880224 0.6332031 +v 0.1876048 0.5342386 0.6183191 +v 0.2343118 0.6180157 0.5688850 +v 0.0402014 0.2654216 0.6385971 +v 0.0511204 0.2092392 0.6276471 +v 0.0979135 0.2147312 0.6205388 +v 0.1179684 0.2700756 0.6228134 +v -0.0755622 0.3263664 0.6576335 +v -0.0017875 0.2820453 0.6359715 +v 0.0217619 0.3179984 0.6657756 +v -0.0850863 0.1827962 0.5985681 +v -0.0258726 0.3079040 0.6595239 +v 0.0185879 0.2147860 0.6444519 +v -0.0047751 0.1610545 0.5868528 +v -0.0960358 0.1585418 0.5735344 +v -0.0351537 0.2098566 0.6241431 +v -0.0374562 0.2510795 0.6343725 +v 0.4964367 0.5909812 0.0709460 +v 0.4370513 0.6561751 -0.0216207 +v 0.4223258 0.6971960 -0.0318582 +v 0.4761614 0.5999428 0.0174382 +v 0.4705171 0.5193364 0.0076672 +v 0.4519136 0.4657613 -0.0169354 +v 0.4857953 0.5642788 0.0312100 +v 0.4411165 0.5043105 -0.0424036 +v 0.4200549 0.4567804 -0.0563299 +v 0.4489838 0.6050171 -0.0453768 +v 0.4330527 0.5273256 -0.0714869 +v 0.3579978 0.4235648 -0.1252003 +v 0.3335243 0.4710425 -0.1601420 +v 0.4001043 0.5887091 -0.1096852 +v 0.4187780 0.6269479 -0.0633081 +v 0.4341889 0.5700007 -0.0728127 +v 0.3984414 0.6615254 -0.0625266 +v 0.3247954 0.4330165 -0.1650518 +v 0.3249694 0.3895490 -0.1502585 +v 0.2879436 0.4421735 -0.1952252 +v 0.4855830 0.4379471 0.0572221 +v 0.4572559 0.4288445 -0.0098345 +v 0.4863454 0.4769006 0.0383541 +v 0.4999591 0.5098367 0.0874660 +v 0.5027810 0.5653445 0.1156508 +v 0.4883143 0.5364462 0.0662681 +v 0.3231657 0.2691328 -0.1210072 +v 0.3588639 0.3089324 -0.1131675 +v 0.2179855 0.0876582 -0.0874011 +v 0.3217489 0.1880943 -0.0723751 +v 0.0637170 0.0704730 -0.0676842 +v 0.0799258 0.0685523 -0.0273512 +v 0.0561878 0.0298189 -0.1029055 +v 0.1102455 0.0337502 -0.0563775 +v -0.1280192 0.6084514 -0.3212087 +v -0.1596146 0.6364039 -0.3084567 +v -0.2458551 0.6494100 -0.2756514 +v -0.1657922 0.5718438 -0.3344340 +v -0.0373046 0.5721052 -0.3203800 +v -0.2250169 0.4705312 -0.3320284 +v -0.2504990 0.3882815 -0.2961171 +v -0.1941282 0.3319431 -0.3017258 +v -0.2615454 0.3358795 -0.2633217 +v -0.2860125 0.2507676 -0.1961257 +v -0.2466675 0.1818881 -0.2132594 +v -0.2303559 0.1356270 -0.2278032 +v -0.3337269 0.1261428 -0.2386030 +v -0.2608205 0.1385725 -0.2286086 +v -0.2692456 0.2790602 -0.2322081 +v -0.2419160 0.2288233 -0.2302126 +v -0.2796425 0.1943698 -0.1929677 +v -0.2939551 0.3283121 -0.2255286 +v -0.2245224 0.3343176 -0.2906910 +v -0.2434302 0.2665251 -0.2462014 +v -0.1892701 0.2528263 -0.2741196 +v -0.3108082 0.3651979 -0.2128899 +v -0.3278383 0.3882267 -0.1628400 +v -0.3071766 0.2930366 -0.1550559 +v -0.3091714 0.3368545 -0.1735958 +v -0.2784326 0.3777593 -0.2601227 +v -0.1731432 0.3996835 -0.3265969 +v -0.2185886 0.3910557 -0.3171418 +v -0.0979932 0.5391916 -0.3262625 +v -0.0804938 0.4729983 -0.3224753 +v -0.1279500 0.4139632 -0.3251222 +v -0.1206207 0.4557803 -0.3275096 +v -0.1249050 0.4985887 -0.3253547 +v 0.0114451 0.5758156 -0.3013126 +v -0.0462146 0.5177773 -0.3204224 +v 0.0311000 0.5157290 -0.2955036 +v 0.0656018 0.6325731 -0.2751484 +v 0.1181109 0.6109361 -0.2597538 +v 0.1902008 0.5976271 -0.2263950 +v 0.1182273 0.5502836 -0.2623181 +v 0.1015036 0.4843207 -0.2821547 +v 0.0418599 0.4775722 -0.2985572 +v -0.0043706 0.5253655 -0.3109128 +v -0.0255375 0.4514108 -0.3072146 +v 0.0309705 0.4353474 -0.2980094 +v 0.1627965 0.5606747 -0.2487844 +v 0.2133869 0.5584535 -0.2244229 +v -0.1670214 0.3598287 -0.3199841 +v -0.2267115 0.4342300 -0.3270009 +v -0.2740712 0.4658512 -0.3055409 +v -0.0835854 1.5896071 -0.2144908 +v -0.1117272 1.6132264 -0.1951491 +v -0.1525177 1.5640519 -0.0907826 +v -0.2327447 1.5282543 -0.0032474 +v -0.2576859 1.5525353 -0.0687691 +v -0.2927244 1.5386544 -0.0436294 +v -0.3752322 1.5011343 0.0105802 +v -0.4175056 1.4285040 0.0008093 +v -0.4027537 1.3860192 -0.0155907 +v -0.2750704 1.2954662 0.0496651 +v -0.2598764 1.3346994 0.1002663 +v -0.2355155 1.3652982 0.0764087 +v -0.1616015 1.3409854 -0.0070379 +v -0.1272431 1.3843999 -0.0780998 +v -0.1034963 1.3947371 -0.0939443 +v -0.1148761 1.4086554 -0.1095106 +v -0.0948414 1.4278963 -0.1398432 +v -0.0713664 1.4529603 -0.1762893 +v -0.0955385 1.4755503 -0.1811469 +v -0.0707292 1.4670728 -0.1887286 +v -0.0678472 1.5133351 -0.2178517 +v -0.0638123 1.5364149 -0.2225943 +v -0.0555077 1.5203968 -0.2030331 +v -0.0938037 1.5756464 -0.1597532 +v -0.1222627 1.6365298 -0.1623446 +v -0.1505097 1.6153442 -0.1546068 +v -0.0864198 1.6071256 -0.1981768 +v -0.0718430 1.5338870 -0.1691080 +v -0.0708724 1.5771620 -0.2027899 +v -0.3670482 1.3009747 0.0053138 +v -0.2652669 1.3691201 -0.0563552 +v -0.3098934 1.4315704 -0.0688942 +v -0.3468527 1.4033035 -0.0505661 +v -0.3508610 1.4710481 -0.0367639 +v -0.3365617 1.2928797 0.0136670 +v -0.3027565 1.3111922 -0.0042574 +v -0.2447794 1.3083813 0.0297313 +v -0.2223120 1.3194880 0.0508912 +v -0.2262586 1.3380883 0.0807627 +v -0.2158303 1.3564136 0.0771795 +v -0.2768206 1.3896281 -0.0642794 +v -0.3793747 1.3667018 -0.0268776 +v -0.3446737 1.3726748 -0.0439513 +v -0.3497029 1.4971279 -0.0238104 +v -0.3153035 1.3749719 -0.0509032 +v -0.3363362 1.3457313 -0.0312663 +v -0.3652548 1.3309003 -0.0159229 +v -0.0689663 1.5624266 -0.2234540 +v -0.1207782 1.5773675 -0.1964727 +v -0.1021845 1.5254807 -0.2041245 +v -0.1017012 1.5082409 -0.1962806 +v -0.1266119 1.5054088 -0.1733914 +v -0.1575474 1.4983304 -0.1492275 +v -0.1864441 1.4743749 -0.1249320 +v -0.1925032 1.3360474 -0.0094131 +v -0.2509785 1.3406386 -0.0317080 +v -0.2663837 1.3392620 -0.0313760 +v -0.2189120 1.3260303 -0.0014647 +v -0.2461201 1.3090997 0.0537728 +v -0.2818218 1.3017908 0.0172603 +v -0.2808283 1.3186406 -0.0092122 +v -0.3283463 1.3177392 -0.0121355 +v -0.2447893 1.3198148 -0.0007442 +v -0.2624949 1.3288522 -0.0183264 +v -0.2813350 1.3365301 -0.0297188 +v -0.3056747 1.3443886 -0.0352206 +v -0.1544210 1.5834537 -0.1627985 +v -0.1922517 1.6237782 -0.0570063 +v -0.1651977 1.6391217 -0.0857805 +v -0.1538743 1.6108863 -0.0765097 +v -0.1786903 1.6248139 -0.1036743 +v -0.1377577 1.6372335 -0.1066812 +v -0.1920350 1.5504810 -0.1309218 +v -0.1784946 1.5410283 -0.1422244 +v -0.2003688 1.5094507 -0.1272969 +v -0.2393929 1.5484048 -0.0908577 +v -0.1954211 1.5730393 -0.1214364 +v -0.1699512 1.5251331 -0.1483891 +v -0.1371860 1.5508212 -0.1784166 +v -0.1413179 1.5279727 -0.1703606 +v -0.1037581 1.5443665 -0.2076953 +v -0.0536772 0.3764098 -0.2983832 +v -0.0745298 0.4233392 -0.3106765 +v 0.0417034 0.5883957 -0.2849717 +v -0.1022494 1.4990047 -0.1152029 +v -0.0920477 1.3846415 -0.0645690 +v -0.1146203 1.3727599 -0.0582655 +v -0.1039501 1.6150856 -0.1714691 +v -0.1078960 1.5400562 -0.1293192 +v -0.0938294 1.5319357 -0.1404680 +v -0.1211721 1.5869726 -0.1270047 +v -0.1315318 1.5282869 -0.1037290 +v -0.0714790 1.4916346 -0.2063931 +v -0.0720965 1.5057447 -0.1489788 +v 0.0762662 0.5339596 -0.2857344 +v 0.0938373 0.5853367 -0.2734783 +v -0.0567327 1.4962144 -0.2052538 +v -0.0543134 1.4786257 -0.1837972 +v -0.0685778 1.4765693 -0.1964972 +v -0.0571131 1.4847693 -0.1993799 +v -0.0555391 1.4601637 -0.1586791 +v -0.0620128 1.4385936 -0.1150880 +v -0.0763681 1.4012392 -0.0889762 +v -0.0839671 1.4132863 -0.1261717 +v -0.0676233 1.4155221 -0.1233085 +v -0.0646050 1.4370567 -0.1572929 +v -0.0573504 1.4610864 -0.1811476 +v -0.0588798 1.4850132 -0.1576717 +v -0.0695657 1.4787606 -0.1330944 +v -0.0801048 1.4542191 -0.1024321 +v -0.0015263 0.3942688 -0.2878200 +v 0.0377060 0.3120140 -0.2734050 +v -0.0017795 0.3129256 -0.2862317 +v 0.0615446 0.3908485 -0.2776778 +v 0.0207622 0.3534451 -0.2919401 +v 0.1136126 0.4371749 -0.2713417 +v 0.1733123 0.5062186 -0.2472379 +v 0.0919032 0.3481978 -0.2704849 +v 0.0929192 0.3067692 -0.2527705 +v 0.1373169 0.3499759 -0.2560522 +v 0.2411163 0.3701894 -0.2204864 +v -0.0687883 0.2429015 -0.2754442 +v -0.0229179 0.2617191 -0.2708209 +v 0.0865638 0.4295733 -0.2864552 +v -0.1977123 0.6575086 -0.2870694 +v -0.2129005 0.6808032 -0.2367294 +v -0.1295624 0.7009156 -0.2280184 +v -0.1829407 0.7051549 -0.1803507 +v -0.1161145 0.6726747 -0.2802811 +v -0.0593406 0.7287451 -0.1757705 +v -0.0790153 0.7023789 -0.2322518 +v -0.7565600 1.4472443 -0.1362913 +v -0.1002069 1.4920363 -0.1882401 +v -0.1262930 1.4730917 -0.1555401 +v -0.1151030 1.4631140 -0.1567765 +v -0.1290112 1.4507596 -0.1387376 +v -0.1260451 1.4873323 -0.1647303 +v -0.1840755 1.4048263 -0.0862871 +v -0.1404859 1.3996297 -0.0924943 +v -0.1449273 1.4423699 -0.1255615 +v -0.1238523 1.4285300 -0.1250148 +v -0.0952838 1.4608713 -0.1699041 +v -0.1031789 1.4438481 -0.1502454 +v -0.1589182 0.6907333 -0.2419992 +v -0.1245817 0.7189413 -0.1753561 +v -0.1159625 0.7583120 -0.1737610 +v 0.1276375 0.5022973 -0.2679000 +v -0.1570754 0.1986385 -0.2555047 +v -0.1364718 0.2382776 -0.2781321 +v -0.0584421 0.2859537 -0.2849636 +v 0.1965541 0.4101245 -0.2455660 +v -0.7627347 1.2736269 -0.1564677 +v -0.7494153 1.2554287 -0.1559670 +v -0.7485407 1.2416667 -0.0705933 +v -0.1043753 1.4710754 -0.0988361 +v -0.0833667 1.4971827 -0.1317799 +v 0.1510114 0.6287064 -0.2413040 +v 0.1090085 0.1855669 -0.2319998 +v 0.1254667 0.1557124 -0.2064372 +v 0.0480011 0.0404947 -0.1819155 +v 0.0199869 0.0291896 -0.1477345 +v 0.0861674 0.2520811 -0.2585676 +v 0.2014879 0.1143099 -0.1694524 +v 0.1339726 0.0909392 -0.1819210 +v 0.1315824 0.0545788 -0.1731661 +v 0.0731805 0.0803487 -0.1868526 +v 0.0771898 0.0340280 -0.1684772 +v 0.2325324 0.1417498 -0.1316901 +v 0.1834113 0.3615373 -0.2439746 +v 0.0740014 0.1781422 -0.2277069 +v -0.0780269 0.0947680 -0.2055461 +v -0.0331062 0.0806595 -0.2016593 +v 0.0067630 0.0380486 -0.1824533 +v -0.0394616 0.0280597 -0.1463985 +v -0.0728805 0.0491438 -0.1938838 +v -0.1334208 0.0843943 -0.2081361 +v -0.1893309 0.0980104 -0.2220068 +v -0.1166952 0.1611912 -0.2386677 +v -0.0991190 0.1271607 -0.2034330 +v 0.2029984 0.3319897 -0.2321261 +v 0.1623541 0.4577202 -0.2488424 +v -0.2026269 1.5919470 -0.0996509 +v -0.1812311 1.5763668 -0.0497757 +v -0.1275234 1.6425323 -0.1400383 +v 0.1855018 0.6610757 -0.1868140 +v -0.1606748 1.4677720 -0.1318288 +v -0.1612005 1.4344558 -0.1137062 +v -0.1568864 1.4091699 -0.0969685 +v -0.1818099 1.4236560 -0.1008139 +v 0.0153372 0.1649312 -0.2393196 +v 0.0591437 0.1378716 -0.2008629 +v 0.0218422 0.0757761 -0.1953609 +v -0.2704712 0.8511753 -0.1013050 +v -0.2969387 0.6536536 -0.1795686 +v -0.2714768 0.7148679 -0.1688215 +v -0.2040771 0.8609255 -0.1115773 +v -0.2098891 0.7489867 -0.1697252 +v -0.2334068 0.8321353 -0.1318679 +v -0.2439211 0.6870733 -0.1833211 +v 0.1779831 0.1828394 -0.1958644 +v 0.1887727 0.2497542 -0.2368711 +v 0.1573194 0.3024646 -0.2390882 +v 0.2185013 0.2833070 -0.2225047 +v 0.2806514 0.2526973 -0.1611673 +v 0.2476355 0.7294921 -0.1515181 +v 0.2728887 0.7994454 -0.0904028 +v 0.2212102 0.8162336 -0.1031552 +v 0.1650783 0.8254605 -0.1229598 +v 0.3289196 0.7513531 -0.0965104 +v 0.2895165 0.7642600 -0.1075227 +v 0.2278125 0.6605436 -0.1767069 +v 0.1580383 0.7268525 -0.1714317 +v 0.1559908 0.6923789 -0.1770105 +v 0.2134549 0.7027123 -0.1724085 +v 0.3020712 0.7112224 -0.1258921 +v 0.2717949 0.6683055 -0.1582691 +v -0.3625776 0.6062255 -0.1707295 +v -0.3398765 0.5746374 -0.1822276 +v -0.3186977 0.6174937 -0.1802501 +v -0.6406793 1.2516338 -0.1569259 +v -0.6337265 1.2522417 -0.1280506 +v 0.1569610 0.2033942 -0.2213605 +v 0.1333005 0.2534702 -0.2478570 +v 0.2066137 0.1601467 -0.1686221 +v 0.2377740 0.1826013 -0.1760368 +v 0.2394415 0.2397568 -0.2121351 +v 0.2835717 0.1934620 -0.1566485 +v 0.2441847 0.2114695 -0.1890872 +v 0.1962027 0.2158676 -0.2212141 +v 0.3162552 0.2337147 -0.1238891 +v 0.2330772 0.6067811 -0.1974033 +v 0.3424242 0.7013628 -0.1009933 +v 0.3603496 0.7661706 -0.0662953 +v 0.4018836 0.7515370 -0.0335545 +v 0.4925781 0.6679481 0.1174267 +v 0.4734081 0.7242897 0.1305909 +v 0.4839744 0.6988499 0.1939600 +v 0.4476191 0.7734163 0.0979348 +v 0.4676726 0.7196652 0.0744789 +v 0.4035752 0.7770250 0.0031945 +v 0.4237687 0.7851631 0.0589238 +v 0.4391204 0.7374205 0.0308273 +v 0.4736770 0.6650960 0.0520829 +v 0.4592958 0.6924177 0.0305403 +v 0.2701617 0.6164474 -0.1803493 +v -0.7871566 1.3803350 -0.1133760 +v -0.7702428 1.2993299 -0.0119573 +v -0.7534617 1.2937982 -0.0519038 +v -0.7633898 1.2699213 -0.0136498 +v -0.7666948 1.2785022 -0.0688621 +v -0.7483358 1.3213209 -0.1227085 +v -0.7724140 1.3148818 -0.0599222 +v -0.7846040 1.3426995 -0.0897258 +v -0.3063022 0.0904879 -0.2361352 +v -0.3048676 0.0461360 -0.2357418 +v -0.3189930 0.1636214 -0.2120705 +v -0.3212431 0.1874036 -0.1771702 +v -0.3599099 0.1769272 -0.1788407 +v -0.0126903 0.7382972 -0.1684887 +v 0.0757770 0.8829463 -0.1218647 +v 0.1235508 0.8479562 -0.1298074 +v 0.1190795 0.8894831 -0.1020017 +v -0.1969034 0.1522332 -0.2189765 +v -0.6144634 1.3376018 -0.0948835 +v -0.2059474 1.4860684 -0.1211715 +v -0.2163921 1.4592421 -0.1094227 +v -0.2911100 1.4158858 -0.0716138 +v -0.2486799 1.5249892 -0.0931236 +v -0.2190974 1.5209162 -0.1156320 +v 0.2449823 0.3203951 -0.2060874 +v 0.2721501 0.3441355 -0.1811426 +v -0.2625994 1.4920369 -0.0906766 +v -0.2885992 1.4862903 -0.0752343 +v -0.3026402 1.5053622 -0.0594116 +v -0.3019349 1.4579278 -0.0723712 +v -0.2582891 0.0767755 -0.2305138 +v -0.0273116 0.1280215 -0.1986970 +v -0.2118018 0.7955838 -0.1545146 +v -0.1085602 0.9304755 -0.0583714 +v -0.1168730 0.9007936 -0.0863796 +v -0.1005990 0.8201509 -0.1461460 +v -0.0604937 0.9181358 -0.0770381 +v -0.0807331 0.9620000 -0.0240222 +v -0.1593352 0.7560483 -0.1664665 +v -0.1992613 1.4439493 -0.1078391 +v -0.3293394 0.0362314 -0.1459942 +v -0.3843628 0.0353451 -0.2207807 +v -0.3916475 0.0587062 -0.2414160 +v -0.3461783 0.0333581 -0.2084744 +v -0.3478438 0.0506939 -0.2479232 +v -0.2509634 0.0488297 -0.2269391 +v -0.4234019 0.0762362 -0.2244330 +v -0.1556548 0.8873101 -0.0951351 +v -0.1094796 0.8615105 -0.1094238 +v -0.1583494 0.8364248 -0.1295156 +v -0.1926449 0.0518573 -0.2143292 +v -0.2612962 0.0331541 -0.1481168 +v -0.2963598 0.0323805 -0.2040233 +v -0.2330223 0.0316800 -0.1909405 +v -0.2578673 0.7599370 -0.1657981 +v -0.2818858 0.8020881 -0.1434610 +v -0.3243318 0.8122613 -0.1189406 +v -0.3112012 0.7618007 -0.1512446 +v 0.1940624 0.7527427 -0.1530502 +v -0.7085233 1.4493498 -0.1145091 +v -0.2367242 1.4206369 -0.0862222 +v -0.2583346 1.4228411 -0.0826455 +v -0.7324993 1.3881673 -0.0051886 +v -0.7447916 1.3511963 0.0435001 +v -0.7324420 1.4185309 -0.0604287 +v -0.6690028 1.3526480 0.0239258 +v -0.5962205 1.1822380 0.0590788 +v -0.5522888 1.2111001 0.0855836 +v -0.4523694 1.2469093 0.0593822 +v -0.4013462 1.2982708 0.0140054 +v -0.4273006 1.3373747 0.0042585 +v -0.4464843 1.3780334 0.0145872 +v -0.4307990 1.4648124 0.0449806 +v -0.4889068 1.3553442 0.1900563 +v -0.5495312 1.3394307 0.1866655 +v -0.4807837 1.1909091 0.2110097 +v -0.4475185 1.2439328 0.1914772 +v -0.4621487 1.2435523 0.2056606 +v -0.4141522 1.3291900 0.1579662 +v -0.3934009 1.3465199 0.1369476 +v -0.3406002 1.3626277 0.1053931 +v -0.2509356 1.3523942 0.0984567 +v -0.4494892 1.2851892 0.2016608 +v -0.4801534 1.2328738 0.2227521 +v -0.4990169 1.1969140 0.2739734 +v -0.5115330 1.2284526 0.3399016 +v -0.5092945 1.1931766 0.3105541 +v -0.4876987 1.1952506 0.2360185 +v -0.5203517 1.2727567 0.3802302 +v -0.4241872 1.4480240 0.0175857 +v -0.2573397 1.4170693 0.0196193 +v -0.2109974 1.3951991 0.0167752 +v -0.1431964 1.3721567 0.0172267 +v -0.1208388 1.3871695 -0.0130997 +v -0.1587746 1.3886389 0.0021851 +v -0.2182725 1.4222676 -0.0052256 +v -0.1950885 1.4145156 -0.0096339 +v -0.1783738 1.3958851 0.0003680 +v -0.1286242 1.3606670 -0.0062197 +v -0.1468869 1.4167697 -0.0338785 +v -0.1429396 1.4618965 -0.0708447 +v -0.1878479 1.5003173 -0.0557537 +v -0.2449083 1.5859354 -0.0307124 +v -0.2779658 1.5644569 0.0015264 +v -0.2616515 1.5622727 0.0312295 +v -0.3615593 1.5157821 0.0242925 +v -0.2198345 1.5946289 -0.0681238 +v -0.3204539 1.5357300 -0.0103156 +v -0.2171655 1.4682244 -0.0291596 +v -0.2897831 1.4752823 0.0477471 +v -0.1791742 1.4451374 -0.0416237 +v -0.1549997 1.4895401 -0.0754220 +v -0.1341677 1.4406581 -0.0608068 +v -0.2669893 1.4627172 0.0103553 +v -0.2529616 1.4390815 0.0052244 +v -0.5052810 1.1657637 0.1073829 +v -0.4877764 1.1623720 0.1333696 +v -0.6453561 1.0191579 0.6138211 +v -0.7476720 0.8186171 0.5053751 +v -0.6730387 0.7913882 0.5056602 +v -0.6551215 0.8248612 0.4989630 +v -0.6577582 0.8654453 0.5851190 +v -0.7490498 0.8695543 0.6295059 +v -0.5985655 0.8304305 0.4957089 +v -0.5310245 0.9646671 0.5047929 +v -0.4909161 1.0323559 0.4418333 +v -0.5047610 1.0859112 0.4247088 +v -0.5234725 1.0681858 0.4563083 +v -0.4656001 1.0727742 0.3606592 +v -0.4179955 0.9733280 0.3433362 +v -0.4666214 0.9952497 0.4200256 +v -0.4513489 1.0025102 0.3813536 +v -0.4675843 1.0528011 0.3918543 +v -0.5624990 0.8641601 0.4731729 +v -0.6186438 0.8889275 0.5371853 +v -0.6402112 0.8869430 0.5952157 +v -0.6919386 0.8779372 0.6399785 +v -0.7403731 0.8961955 0.6525699 +v -0.6352456 0.8277027 0.4982447 +v -0.6570020 0.8435018 0.5050622 +v -0.6919672 0.8212137 0.4943931 +v -0.7162095 0.8264557 0.5139759 +v -0.6790568 0.9768063 0.6642615 +v -0.4466118 1.0750471 0.2151878 +v -0.4875181 1.1381834 0.3084489 +v -0.4806442 1.1517861 0.2625047 +v -0.4715473 1.0802327 0.1241807 +v -0.5090204 1.1953022 0.0975809 +v -0.4977950 1.1852609 0.1099689 +v -0.4747479 1.1902622 0.1363738 +v -0.4361909 1.2145307 0.1435776 +v -0.4452520 1.2292466 0.1789832 +v -0.3654909 1.3021230 0.1480189 +v -0.3186987 1.2785667 0.0493965 +v -0.6211551 1.2181038 0.0165718 +v -0.6198364 1.2584505 0.0556567 +v -0.6259766 1.2861520 0.0242240 +v -0.6177158 1.2422962 0.0335889 +v -0.6054878 1.2432983 0.0727811 +v -0.5985522 1.2866616 0.1059330 +v -0.6462935 1.3186326 0.1888855 +v -0.6717929 1.3261880 0.2379871 +v -0.7592034 1.3446375 0.0295810 +v -0.6461021 1.3006184 0.1013580 +v -0.7115689 1.3381802 0.0968889 +v -0.6582113 1.3125598 0.1319003 +v -0.6091518 1.3069266 0.1338936 +v -0.6273317 1.2866206 0.0962309 +v -0.6132716 1.2687846 0.0889858 +v -0.6078116 1.2223948 0.0582168 +v -0.8405920 1.3077396 0.2704276 +v -0.7654020 1.3068695 0.1039645 +v -0.7532825 1.3241080 0.2239389 +v -0.5917653 1.3208016 0.2173500 +v -0.6070826 1.3190248 0.1918317 +v -0.5031650 1.3666006 0.1776667 +v -0.4582307 1.3914580 0.1642481 +v -0.6318725 1.3124816 0.1520603 +v -0.5754083 1.1951904 0.0775763 +v -0.5278136 1.2087113 0.0912771 +v -0.4955507 1.2260528 0.0792286 +v -0.4886512 1.2449796 0.0675266 +v -0.4495587 1.3369249 0.0157746 +v -0.4863234 1.4135420 0.0723735 +v -0.4889278 1.3555593 0.0461959 +v -0.4627865 1.3990620 0.0330307 +v -0.4611145 1.4412669 0.0944751 +v -0.4684805 1.4229748 0.1260396 +v -0.5566598 1.2757225 0.0882705 +v -0.5734119 1.2428275 0.0866512 +v -0.5838027 1.2617821 0.0939646 +v -0.5517114 1.3044704 0.0901730 +v -0.5339326 1.3726350 0.1083744 +v -0.5241268 1.3386631 0.0738520 +v -0.5026654 1.4041108 0.1074042 +v -0.5010796 1.3794277 0.0651745 +v -0.4742008 1.3369673 0.0326131 +v -0.3985071 1.3269560 -0.0032523 +v -0.4372928 1.2961375 0.0263470 +v -0.4685478 1.2936766 0.0403822 +v -0.4957741 1.3035830 0.0513242 +v -0.4985865 1.3236158 0.0517306 +v -0.5250652 1.2919879 0.0710445 +v -0.5446584 1.2405570 0.0832486 +v -0.5364543 1.2622106 0.0784297 +v -0.5813833 1.2174882 0.0800078 +v -0.6053016 1.1993349 0.0564275 +v -0.5766669 1.2960660 0.1043471 +v -0.5537637 1.3295368 0.1016396 +v -0.5652069 1.3425862 0.1269616 +v -0.5600197 1.3434142 0.1701681 +v -0.5855165 1.3243883 0.1655516 +v -0.5824005 1.3132675 0.1208851 +v -0.5325717 1.3765882 0.1409325 +v -0.5033147 1.3826929 0.1598551 +v -0.6929579 1.3282596 0.2688573 +v -0.6400115 1.3258086 0.2607363 +v -0.7018628 1.3835390 0.0077621 +v -0.2464603 1.4524157 -0.0972472 +v -0.2585748 1.4692454 -0.0948043 +v -0.1608346 1.5169373 -0.0781428 +v 0.1714248 0.1442256 -0.1913615 +v -0.0645558 0.8538057 -0.1363162 +v -0.2147156 1.4113495 -0.0849779 +v -0.2291255 1.3823875 -0.0657013 +v -0.2483335 1.3553867 -0.0458067 +v -0.4070310 0.0341686 -0.1755043 +v -0.4554158 0.0784888 -0.2018512 +v -0.3041869 0.2069725 -0.1604636 +v 0.3771071 0.5447394 -0.1342204 +v 0.4054450 0.5325788 -0.1107058 +v 0.3776447 0.4839976 -0.1123586 +v 0.3280401 0.5719467 -0.1542045 +v 0.3119533 0.6099149 -0.1607881 +v -0.0854692 1.4213573 -0.0701406 +v 0.0205073 0.1299663 -0.1970454 +v 0.0965712 0.1145359 -0.1938539 +v -0.3126198 0.2386127 -0.1350040 +v -0.3571000 0.3679544 -0.0742927 +v -0.3346836 0.3489003 -0.1107651 +v 0.0784963 0.8371893 -0.1408384 +v -0.1973729 1.3929989 -0.0755133 +v -0.1710338 1.3781563 -0.0677554 +v -0.1917777 1.3585875 -0.0459958 +v -0.2085038 1.3718293 -0.0589782 +v -0.2296878 1.3572092 -0.0464602 +v -0.1616961 1.3617867 -0.0486781 +v -0.2214472 1.3455836 -0.0333220 +v 0.3061551 0.5118166 -0.1711230 +v 0.3523856 0.5169832 -0.1436323 +v 0.3741380 0.6488388 -0.0952567 +v 0.3066470 0.6673898 -0.1444651 +v 0.3732616 0.6036998 -0.1255733 +v 0.3438006 0.6457579 -0.1282133 +v 0.2260522 0.7766967 -0.1149992 +v 0.1562595 0.8657640 -0.1048819 +v -0.6565456 1.3988940 -0.0731107 +v -0.6745393 1.3784626 -0.0090835 +v -0.6665778 1.4278075 -0.1140334 +v -0.4202091 0.4906172 -0.1111043 +v -0.4525625 0.5295182 -0.1211996 +v -0.4728077 0.5040380 -0.1103287 +v -0.5146765 0.5961120 -0.1363058 +v -0.4942078 0.5513267 -0.1318001 +v -0.5802155 0.6366632 -0.1366512 +v -0.4316060 0.7648357 -0.1334207 +v -0.0910426 1.3972850 -0.0508590 +v -0.1206303 1.4069209 -0.0344193 +v -0.0992095 1.4379548 -0.0762096 +v -0.1863971 1.6118381 -0.0315340 +v 0.3219785 0.3105262 -0.1465072 +v -0.0273467 0.8762688 -0.1069616 +v -0.0110044 0.9020309 -0.0885538 +v -0.4370346 0.1468577 -0.1533945 +v -0.4815526 0.0810922 -0.1472940 +v -0.6318608 0.7029418 -0.0944405 +v -0.6880885 0.7781559 -0.0592200 +v -0.6695917 0.8160530 -0.0480237 +v -0.7144482 0.8342559 -0.0167964 +v -0.7455398 0.7833223 -0.0246972 +v -0.6661847 0.7333541 -0.0797131 +v -0.7773355 0.7046418 -0.0364512 +v -0.7258818 0.7157506 -0.0619696 +v -0.7182032 0.7489379 -0.0526749 +v -0.7015577 0.6615292 -0.0752932 +v -0.6142224 0.7246038 -0.1012787 +v -0.6172614 0.8419232 -0.0720685 +v -0.6433872 0.7995977 -0.0774769 +v -0.4912054 0.0852159 -0.0930478 +v -0.5916218 0.0356980 -0.0150889 +v -0.6188006 0.0624550 -0.0215441 +v -0.5727975 0.0597598 -0.0399388 +v -0.5081685 0.0642335 -0.0590985 +v -0.4939932 0.0364596 -0.0731251 +v -0.4364799 0.0356258 -0.1157218 +v -0.4676799 0.0379477 -0.1633375 +v -0.4858164 0.0383961 -0.1116737 +v -0.6754902 0.6828477 -0.0902981 +v -0.6246394 0.6542543 -0.1050191 +v -0.3343955 1.4555953 -0.0539669 +v -0.3663729 1.4203863 -0.0374506 +v -0.4030417 1.4171898 -0.0139989 +v -0.1875103 0.0344970 -0.1383341 +v -0.1615503 0.0386102 -0.1934367 +v -0.4376065 0.8710285 -0.0811902 +v -0.4762599 0.8754881 -0.0795285 +v -0.5869381 0.8754289 -0.0688620 +v -0.6599336 0.8683670 -0.0348580 +v -0.1969920 1.5291560 -0.0479914 +v -0.2075526 1.5803298 -0.0073282 +v -0.2245531 1.5902880 -0.0007438 +v -0.2352504 1.5661986 0.0201155 +v -0.1137808 1.3672651 -0.0395215 +v -0.1403138 1.3621110 -0.0466756 +v -0.4614626 0.1316379 -0.0996164 +v -0.6739151 1.1991363 -0.0357988 +v -0.6775503 1.1863517 -0.0022925 +v -0.6351501 1.1972529 -0.0010664 +v -0.6290284 1.1914806 0.0219269 +v -0.6482918 1.1946654 -0.0119957 +v -0.6307729 1.2091014 -0.0223474 +v -0.6179736 1.2547284 -0.0148542 +v -0.6203308 1.2875340 -0.0117252 +v -0.6239595 1.2387455 -0.0421747 +v -0.3689422 1.4961189 -0.0056312 +v -0.4476494 0.4417562 -0.1035982 +v -0.0899794 0.0820599 -0.0887798 +v -0.1452442 0.0755782 0.0653602 +v -0.1063623 0.0792802 0.0084841 +v -0.1047730 0.0360566 -0.1238779 +v -0.1447426 0.0821531 -0.0959537 +v -0.6749373 0.6067834 -0.0999592 +v 0.4143526 0.3499052 -0.0638891 +v 0.4252769 0.3183114 -0.0252527 +v 0.3824068 0.3724997 -0.1042705 +v -0.7782390 1.3252172 -0.0357886 +v -0.7717149 1.2879329 0.0439141 +v -0.7573492 1.2732650 0.0164897 +v -0.7616445 1.3781891 -0.0191039 +v -0.2432220 1.4968508 -0.0081756 +v -0.2698400 1.5358407 0.0515024 +v 0.3021555 0.1822811 -0.1199004 +v -0.7171741 0.5976238 -0.0818329 +v 0.1358052 0.9769526 -0.0081774 +v 0.0856835 0.9892899 -0.0066264 +v 0.0021995 0.9483699 -0.0433250 +v -0.0174352 0.9855061 -0.0158067 +v -0.7654839 1.2518059 -0.0177973 +v -0.7363740 1.2112347 -0.0179881 +v -0.7477424 1.2158513 0.0057408 +v -0.7724218 1.2473252 0.0351259 +v -0.7665433 1.2200768 0.0547685 +v -0.7853986 1.2676606 0.0941862 +v -0.8100578 1.2767726 0.1312438 +v -0.7078386 1.1907926 -0.0056877 +v -0.7320497 1.1856812 0.0275722 +v -0.7671545 1.1109934 0.0415728 +v -0.8151938 1.1172518 0.0761422 +v -0.7504481 1.1577717 0.0399401 +v -0.7806414 1.2476244 0.0746485 +v -0.6558053 1.1808629 0.0115723 +v -0.6558540 1.1687332 0.0227725 +v -0.6449512 1.1516541 0.0267274 +v -0.6979137 1.1708533 0.0213250 +v -0.7162622 1.1459185 0.0296836 +v -0.6855909 1.1467770 0.0244622 +v -0.7384288 1.1171408 0.0308071 +v -0.6351587 1.3445752 -0.0311508 +v 0.1586108 0.0405934 -0.1421716 +v -0.1603753 0.0798315 0.0027492 +v -0.2069171 0.0802683 -0.1082206 +v 0.2660038 0.1463391 -0.1097028 +v 0.4019488 0.4750124 -0.0784697 +v -0.7395668 0.6724435 -0.0635199 +v -0.7980179 0.6548463 -0.0261538 +v -0.7656050 0.6150694 -0.0585424 +v 0.0164861 0.0822160 -0.0201210 +v 0.0712840 0.0310632 0.0364648 +v 0.0485417 0.0231672 0.1121481 +v -0.4221454 0.1767456 -0.0913010 +v 0.3861118 0.4361395 -0.0884860 +v -0.6770633 0.4880859 -0.0825922 +v -0.7074528 0.4755208 -0.0578222 +v -0.2715708 0.9096360 -0.0591914 +v -0.2146783 1.3211441 0.0168443 +v -0.1877407 1.3305199 0.0213425 +v -0.1973152 1.3415672 0.0600410 +v -0.1734733 1.3441324 0.0383396 +v 0.4069485 0.2971103 -0.0503880 +v 0.1705929 0.0328561 -0.1062023 +v -0.0463949 0.0821083 -0.0807935 +v 0.0032921 0.0790641 -0.0792759 +v 0.4301032 0.4109761 -0.0544407 +v -0.7140332 1.3609627 0.0494547 +v -0.6861513 1.3497436 0.0538759 +v -0.6637861 1.3266044 0.0599816 +v -0.6766853 1.3227636 0.1029969 +v -0.6483011 1.3059629 0.0684070 +v -0.6456589 1.3198522 0.0279591 +v -0.3638427 1.4918797 0.0873862 +v -0.2843710 1.5023681 0.0589625 +v -0.2950558 1.5210327 0.0687371 +v -0.4016715 0.0526704 -0.0586188 +v -0.4315017 0.0362945 -0.0438247 +v -0.3675790 0.0819778 -0.0664962 +v -0.4294693 0.0409791 0.0156743 +v -0.3934987 0.0762614 -0.0132360 +v -0.3588399 0.0810425 0.0240000 +v -0.3769026 0.0629862 0.0512092 +v -0.3329466 0.0823615 -0.0251689 +v -0.1783947 0.0736683 0.1406559 +v 0.4593699 0.5671233 -0.0303993 +v -0.3305796 0.9588727 -0.0002625 +v -0.3131450 0.9863610 0.0591886 +v -0.3370185 1.0133914 0.1139473 +v -0.3740633 1.0179844 0.0769943 +v -0.3091064 1.0022199 0.1235253 +v -0.3093769 1.0094905 0.2196404 +v -0.2769751 0.9773204 0.0649167 +v -0.6318144 1.3158727 -0.0017260 +v -0.7976536 0.7370847 -0.0006189 +v -0.4373405 1.0008098 0.0050251 +v -0.3609003 1.2688000 0.0399695 +v -0.3209969 1.2748282 0.0757310 +v -0.3148005 1.2916408 0.0203923 +v -0.3443407 1.2793347 0.0298384 +v -0.0372042 0.0794199 0.0160191 +v 0.0111115 0.0781897 0.0357556 +v -0.2499729 1.3827642 0.0475027 +v -0.2837374 1.3739769 0.0696982 +v -0.2835515 1.3963716 0.0474291 +v -0.3419763 1.3880823 0.0850413 +v -0.3736335 1.3737855 0.1162289 +v -0.4022534 1.4089022 0.1512261 +v -0.4451166 1.3724996 0.1743701 +v -0.4067336 1.3693919 0.1575275 +v -0.3660891 1.4059972 0.1242958 +v -0.4017698 1.4389031 0.1363594 +v -0.4496813 1.4221338 0.1419520 +v -0.4214276 1.4735463 0.0695256 +v -0.2001840 1.3763065 0.0385454 +v -0.2989847 1.4429610 0.0399179 +v -0.3179516 1.4198867 0.0557819 +v -0.3378743 1.4607794 0.1124355 +v -0.4065512 1.4621428 0.1105473 +v -0.3238407 1.4883385 0.0971409 +v -0.3088571 1.5401419 0.0381374 +v -0.3366801 1.5195129 0.0567414 +v 0.1925419 0.0317010 0.0032861 +v -0.3778499 1.2466829 0.0751170 +v -0.3634293 1.2699777 0.1274316 +v -0.3323603 1.2931626 0.1167637 +v -0.3590110 1.2885541 0.0157433 +v -0.3817313 1.2635393 0.0401601 +v -0.3464917 1.2673714 0.0506564 +v -0.3499079 1.2599398 0.0719964 +v -0.3584786 1.2599623 0.1107293 +v 0.4632688 0.3345349 0.0371225 +v 0.4916814 0.3314907 0.0353398 +v 0.5220965 0.3733281 0.0687782 +v 0.6014006 0.3873135 0.0891522 +v 0.6502427 0.3776173 0.2044386 +v 0.6078433 0.4179229 0.2164934 +v 0.5080488 0.5268003 0.1414869 +v 0.5050325 0.6083493 0.1358085 +v 0.4674918 0.3927460 0.0365111 +v 0.4429180 0.3590317 -0.0137976 +v 0.4767670 0.3619674 0.0569142 +v -0.4122428 1.2737038 0.0323397 +v -0.3883616 1.2563572 0.1406773 +v -0.3825113 1.2799096 0.1489254 +v -0.3679352 1.2500203 0.0949778 +v -0.4028852 1.2375283 0.0738218 +v -0.4444011 1.2210966 0.0837719 +v -0.4859520 1.2160294 0.0880106 +v -0.3976660 1.2356169 0.1076445 +v -0.4469731 1.2164401 0.1674144 +v -0.4274668 1.2357368 0.1652527 +v -0.4289341 1.2584808 0.1796208 +v -0.4141254 1.2351990 0.1490390 +v -0.3895050 1.2425017 0.1224445 +v -0.4195261 1.2224076 0.1120037 +v -0.4812568 1.1933072 0.1199025 +v -0.4435170 1.2105175 0.1100935 +v -0.4877592 1.2015265 0.1035945 +v -0.6562124 1.0970817 0.0181067 +v -0.5467127 1.1378328 0.0635106 +v -0.5707775 1.1691548 0.0590130 +v -0.5557110 1.1748608 0.0737473 +v -0.5385672 1.1139675 0.0674095 +v -0.5696689 1.1230590 0.0438362 +v -0.5957587 1.1362262 0.0355243 +v -0.5958466 1.1095334 0.0318741 +v -0.5467260 1.1583098 0.0711426 +v -0.7839441 0.5358236 -0.0127337 +v -0.8222982 0.5605158 0.0180636 +v -0.8229299 0.5167389 0.0400824 +v 0.3701597 0.8729516 0.0611755 +v 0.4053666 0.8249276 0.0693822 +v -0.4724540 0.1471354 -0.0373720 +v -0.5481555 0.1236432 -0.0258416 +v -0.5653478 0.1671852 0.0122068 +v -0.5173787 0.2184794 -0.0035804 +v -0.5162982 0.1645105 -0.0156115 +v -0.7702219 0.8331712 0.0176963 +v -0.6293803 1.2832640 0.0662516 +v -0.2869228 1.3038594 0.0896417 +v -0.2808918 1.3462235 0.1168047 +v 0.1413626 0.0258830 0.0014657 +v 0.3595934 0.2025893 -0.0235554 +v 0.3256607 0.1712766 -0.0272610 +v 0.3869220 0.1630588 -0.0165996 +v 0.3859584 0.2298401 -0.0188701 +v 0.3970820 0.1194289 0.0028103 +v 0.4695196 0.1414511 0.0092531 +v -0.7649153 1.0245210 0.0202834 +v -0.1785564 1.0121895 0.2005451 +v -0.1317854 1.0219301 0.1667188 +v -0.1669363 1.0090003 0.2738424 +v -0.1032465 0.9958228 0.3651089 +v -0.0466629 1.0263487 0.3327319 +v 0.0253964 0.9358534 0.4670675 +v -0.8046951 1.2041291 0.0904996 +v -0.7881781 1.1529433 0.0557843 +v -0.7513175 1.1963704 0.0418747 +v -0.4672178 1.2708133 0.0513546 +v -0.1761703 1.3646728 0.0466796 +v -0.5300254 0.0358701 -0.0394662 +v -0.4843120 0.0336026 0.0058442 +v -0.5636199 0.0334058 0.0444266 +v -0.3400460 0.2192347 -0.0303896 +v -0.2958131 0.0790346 0.0180041 +v -0.3160106 0.0554794 0.1452087 +v -0.2476459 0.0753178 0.1112325 +v -0.3202004 0.0729539 0.0899601 +v 0.0457259 0.0653576 0.0325582 +v -0.5639034 0.2726967 0.0275057 +v -0.4198908 1.2478787 0.0540285 +v -0.5021892 1.2725282 0.0625940 +v -0.5215188 1.2417963 0.0785956 +v -0.2145655 0.0805930 -0.0309469 +v 0.4441668 0.1860209 -0.0232210 +v 0.4527304 0.2476553 -0.0135549 +v -0.6199766 0.3540802 0.0284354 +v 0.2613134 0.9563459 0.0521226 +v 0.2145340 0.9993552 0.0841991 +v 0.2491625 0.9858433 0.1052581 +v -0.8041055 1.1690321 0.0718754 +v -0.2608788 1.3177915 0.0863259 +v -0.3296332 1.2801808 0.1040455 +v -0.3199323 1.3134611 0.1209769 +v -0.2951520 1.2925445 0.0807720 +v -0.7426794 1.3252264 0.1021773 +v -0.7592444 1.3130853 0.1493201 +v -0.7224346 1.3228325 0.1378489 +v -0.6906944 1.3228687 0.1340689 +v -0.2033724 0.0763340 0.0572439 +v -0.5613992 0.2003484 0.0238086 +v -0.5198246 0.2653131 -0.0113086 +v -0.5484251 0.2446662 0.0163173 +v -0.6092066 0.3872111 -0.0113421 +v -0.7260368 0.3761064 0.0857899 +v -0.7791063 0.3966870 0.2136850 +v -0.7926738 0.4078051 0.2601880 +v -0.6987381 0.3227558 0.2498742 +v -0.6582326 0.3073337 0.2564036 +v -0.7331141 0.3491051 0.2847902 +v -0.7645442 0.3879534 0.2839902 +v -0.6766479 0.3845104 0.0269510 +v -0.6622093 0.3381927 0.0674204 +v -0.7426320 0.3569366 0.2225902 +v -0.7067464 0.3289817 0.1761002 +v -0.6009317 0.3273919 0.0570968 +v -0.6209560 0.3145728 0.1051357 +v -0.6598854 0.3211022 0.1264547 +v -0.8643116 0.6595363 0.0502795 +v -0.7256825 1.3236232 0.1950075 +v -0.6861440 1.3215080 0.1814643 +v -0.7909911 1.3095948 0.1830874 +v 0.3310047 0.1062118 0.0030123 +v 0.3199083 0.0494828 0.0548603 +v 0.4629820 0.1078190 0.0549729 +v 0.4107285 0.0711254 0.0570672 +v 0.4295394 0.1077018 0.0248448 +v 0.3535004 0.0631191 0.0409317 +v 0.3864004 0.0852264 0.0248740 +v 0.2777808 0.1139170 -0.0110219 +v 0.2892046 0.0358039 0.0701283 +v 0.2648754 0.0206759 0.1423607 +v -0.6046679 0.1052542 -0.0108712 +v 0.5315128 0.3359586 0.0314992 +v -0.3019410 1.3579092 0.1047883 +v -0.3470138 1.3361343 0.1462010 +v 0.5753174 0.2770245 0.0171096 +v 0.5652862 0.3202343 0.0288077 +v -0.2965408 1.3307209 0.1195120 +v -0.3141561 1.3449956 0.1311874 +v -0.7204399 0.4056685 0.0476884 +v -0.8690277 0.5755579 0.0740548 +v -0.8546252 0.6026118 0.0505360 +v -0.8864394 0.6650449 0.1372573 +v -0.3875282 1.0299218 0.1564460 +v -0.5270416 1.1555548 0.0896122 +v -0.5122082 1.1427410 0.1018242 +v -0.5056927 1.1054531 0.0877306 +v -0.3566762 1.4374924 0.1264220 +v 0.5368615 0.1785304 0.0260506 +v -0.9274690 1.0258386 0.1937386 +v -0.9486784 1.0225443 0.2822866 +v -0.9050446 1.0123610 0.1531719 +v -0.9339769 0.9080787 0.2105508 +v -0.9363649 0.9699658 0.2034178 +v -0.9558785 0.9989958 0.3337764 +v -0.8962153 1.0661705 0.1433026 +v 0.2984092 0.0749178 0.0186211 +v -0.1558200 1.0124668 0.1304820 +v -0.0446671 1.0405339 0.2005750 +v -0.3654513 1.3433739 0.1342275 +v -0.3519361 1.3196925 0.1474407 +v -0.3732781 1.3271540 0.1561682 +v 0.0890453 0.0389041 -0.0012520 +v 0.0038345 0.0704864 0.0880594 +v 0.0355328 0.0334161 0.0842258 +v -0.0522493 0.0759067 0.0796758 +v -0.7672890 0.4129623 0.0948638 +v -0.8443357 0.4816837 0.2222146 +v -0.7983795 0.4227150 0.1581116 +v -0.8154422 0.4247563 0.2237625 +v -0.7732890 0.3866700 0.1594696 +v -0.2348060 0.9818189 0.0733086 +v -0.2106666 1.0034908 0.2286195 +v -0.2408971 0.9945554 0.1404224 +v -0.2009577 1.0045477 0.1495707 +v -0.1182204 1.0243250 0.2643277 +v -0.0929399 1.0214096 0.3107453 +v -0.7246695 0.0878788 0.0672426 +v -0.7064596 0.1241947 0.0741654 +v -0.5474160 0.0336675 0.2228227 +v -0.5041391 0.0397047 0.2681938 +v -0.5027228 0.0366579 0.3143398 +v -0.6269183 0.0350107 0.0391052 +v -0.5527828 0.0330577 0.1307644 +v -0.5980194 0.0341389 0.0880219 +v -0.5095909 0.0334679 0.0595844 +v -0.5443838 0.0328707 0.2992548 +v -0.7536207 0.0577066 0.1687504 +v -0.7239582 0.0387943 0.1393401 +v -0.6629856 0.0358977 0.1503914 +v -0.6137878 0.0342386 0.1830631 +v -0.6796181 0.0360641 0.0620455 +v -0.6113884 0.1864476 0.1023135 +v -0.6629477 0.1712176 0.1159517 +v -0.6665363 0.1501662 0.0581035 +v -0.4810583 1.1602854 0.1650635 +v -0.8201151 1.2228478 0.1126488 +v -0.8506981 1.2579017 0.1722965 +v -0.3985952 1.3104864 0.1702898 +v -0.4279721 1.3134862 0.1739483 +v -0.6955146 0.3566747 0.0672938 +v -0.7314032 0.3515545 0.1511199 +v -0.4595923 1.0613528 0.1256422 +v -0.8788267 0.6274503 0.0942619 +v -0.8429039 1.1854857 0.1457387 +v -0.8750567 1.1457143 0.1613603 +v -0.7260000 0.0720000 0.0473000 +v -0.7265000 0.0765000 0.0530000 +v -0.7260000 0.0766000 0.0513000 +v -0.7251000 0.0731000 0.0497000 +v 0.5054545 0.4005152 0.0939984 +v -0.4761137 1.1866478 0.1584473 +v -0.4713541 1.2103113 0.1944374 +v -0.4646726 1.2047877 0.1763714 +v -0.4553896 1.2189934 0.1815004 +v -0.4291841 1.2816238 0.1867581 +v 0.6041926 0.2906332 0.0531648 +v -0.4826728 1.1196860 0.1348702 +v -0.0995531 0.0751473 0.0902948 +v -0.0004220 0.0468961 0.1382823 +v -0.0486344 0.0691773 0.2656749 +v -0.0274130 0.0685351 0.1993914 +v -0.0315595 0.0724530 0.1487900 +v -0.5863001 0.2791527 0.1453410 +v -0.8676038 1.1932273 0.2013339 +v -0.8877827 1.1391835 0.2119052 +v -0.4088890 1.2510287 0.1580014 +v -0.4237084 1.0348070 0.2552014 +v -0.4581241 1.0830668 0.3163103 +v -0.5032906 1.1332476 0.3546605 +v -0.4498440 1.0816635 0.2674584 +v -0.4044763 1.0216663 0.2568146 +v -0.4467783 1.0308511 0.3531979 +v -0.3662860 0.9960613 0.3191467 +v -0.3765702 1.0134306 0.2694751 +v -0.4223896 0.9921624 0.3160926 +v -0.4725809 1.1344604 0.1979625 +v -0.4517419 1.3448902 0.1894497 +v -0.4345191 1.3367473 0.1794496 +v -0.8956346 0.7072639 0.1435083 +v -0.9026694 0.6738291 0.1818462 +v 0.5444415 0.4002005 0.0981226 +v 0.5237532 0.4228861 0.2057689 +v 0.4340823 0.4922371 0.4294719 +v 0.4543148 0.5181046 0.4060099 +v 0.5078065 0.4732255 0.2716586 +v 0.3736786 0.2463295 0.4531595 +v 0.3997500 0.2066837 0.3852503 +v -0.4753739 1.1142834 0.1547997 +v -0.5737669 1.1594672 0.4534878 +v -0.5387816 1.1529135 0.4229831 +v -0.5160416 1.1750656 0.3789980 +v -0.4741586 1.1490628 0.2239442 +v 0.0918803 1.0491623 0.1801163 +v 0.1312863 1.0446099 0.2072871 +v 0.1700871 1.0313106 0.2116262 +v -0.5572713 1.3143986 0.2729723 +v -0.5236486 1.2922760 0.3267512 +v -0.5122411 1.2789332 0.2956424 +v -0.5158983 1.2952095 0.2421462 +v -0.5417517 1.3281900 0.2057176 +v -0.5750741 1.3194972 0.2475082 +v -0.5528634 1.3047813 0.3649288 +v -0.5348398 1.3081608 0.2440523 +v -0.6618522 0.3082763 0.2034222 +v 0.5036793 0.5858548 0.1914918 +v 0.5098544 0.4778230 0.1766021 +v 0.5014823 0.4646308 0.1002465 +v 0.5114869 0.4410879 0.2271107 +v 0.5064914 0.5371810 0.1926759 +v 0.5031158 0.5314136 0.2808225 +v -0.4752064 1.2745792 0.2187197 +v -0.4999839 1.2642823 0.2600879 +v -0.5120364 1.3242748 0.2151233 +v -0.4823712 1.3292115 0.2054240 +v -0.4984589 1.2877525 0.2293516 +v -0.6966382 0.1574254 0.1532883 +v 0.4677278 0.7423114 0.1800480 +v -0.4637570 1.0955260 0.1879275 +v -0.4655449 1.1176610 0.2327819 +v -0.0113582 1.0430042 0.2459064 +v 0.0106754 1.0294540 0.3383246 +v 0.1279785 1.0153788 0.3591190 +v 0.1791631 0.9748462 0.3977726 +v 0.1573673 0.9364308 0.4418373 +v 0.1949887 0.9966114 0.3552126 +v -0.4635793 1.3101989 0.2052263 +v -0.8775605 0.8145550 0.2050804 +v -0.4348294 0.0313349 0.1498616 +v -0.7142739 0.0590764 0.2202241 +v -0.7114669 0.0416208 0.1932905 +v 0.5128901 0.4211542 0.1402420 +v 0.4778915 0.4412301 0.3453359 +v 0.4333165 0.3365175 0.4187404 +v 0.4112835 0.2729198 0.4175208 +v 0.4428464 0.4251666 0.4075167 +v 0.3557959 0.9051561 0.3080716 +v 0.3741820 0.8889783 0.2652484 +v -0.6372386 1.3140223 0.3979032 +v -0.6852901 1.2677130 0.5017601 +v -0.6200740 1.2958938 0.4461916 +v -0.7807295 1.3227303 0.2833628 +v -0.4956827 0.0314421 0.1433362 +v 0.4913082 0.6607739 0.2481194 +v -0.8761908 0.8338590 0.2657130 +v -0.8723841 0.8387060 0.1972467 +v -0.8795903 0.8055037 0.2893513 +v -0.9072515 0.8625668 0.2477291 +v 0.1179080 1.0376565 0.2936423 +v -0.4912398 1.2344629 0.2461825 +v -0.4994126 1.2455568 0.2864002 +v -0.2581921 0.0735618 0.1599361 +v -0.3606939 0.0389138 0.2778682 +v -0.3502241 0.0298065 0.3233159 +v -0.2890619 0.0466784 0.2568961 +v -0.3086980 0.0314349 0.2275885 +v -0.3112983 0.0395728 0.3178915 +v -0.2761185 0.0651659 0.2876109 +v -0.3301558 0.0357978 0.3691366 +v -0.3269679 0.0283870 0.2868038 +v -0.3806141 0.0438681 0.3340673 +v -0.4330550 0.0414079 0.3193054 +v -0.4541351 0.0374022 0.3360785 +v -0.4621769 0.0310880 0.3684088 +v -0.3963142 0.0324988 0.3634163 +v -0.2267372 0.0703260 0.2454768 +v -0.3794306 0.0260925 0.1581445 +v -0.8356000 0.4760891 0.2836495 +v -0.9517267 0.9583542 0.2588120 +v 0.1677693 1.0288613 0.2770939 +v -0.6063364 0.1854280 0.1572466 +v -0.5054853 0.0451712 0.1742516 +v -0.5466581 0.0325789 0.1773292 +v 0.5995156 0.1635957 0.2174292 +v 0.5799655 0.1540720 0.2615364 +v 0.5671217 0.1767558 0.3102925 +v 0.6300354 0.2082403 0.2269934 +v 0.6510325 0.2815934 0.2400009 +v 0.6353028 0.3295622 0.2903308 +v -0.9321797 1.0757557 0.3025784 +v -0.3486569 0.0254713 0.1882872 +v -0.6623431 0.0398277 0.2432643 +v -0.6172884 0.0347862 0.2766674 +v 0.5537981 0.1313940 0.2621689 +v -0.5617439 0.2740925 0.2002490 +v -0.8205078 0.4341598 0.2743730 +v -0.4341028 1.0394058 0.3108194 +v -0.8900274 1.2825040 0.3314140 +v 0.3819194 0.0358405 0.1704925 +v 0.4377186 0.0692546 0.2473657 +v 0.4650006 0.0791412 0.2387731 +v 0.5113088 0.1159045 0.2935838 +v 0.4360401 0.0859052 0.3113236 +v 0.4312256 0.0595508 0.1959544 +v -0.5278288 0.2565873 0.2260310 +v -0.3000626 0.0466035 0.1914469 +v -0.2617792 0.0664957 0.2390332 +v 0.3469951 0.8781902 0.3734287 +v -0.7876076 1.3133388 0.3652134 +v -0.8294268 1.3076019 0.3341667 +v -0.8600461 1.2673355 0.4171115 +v -0.5864745 1.3199385 0.3091308 +v 0.3232459 0.0249102 0.2034321 +v -0.1637713 0.9854112 0.3349684 +v -0.5396920 0.1877782 0.2253541 +v -0.3172126 0.9942591 0.2978332 +v -0.3376382 0.9564785 0.3826175 +v -0.2551559 0.9257107 0.3978846 +v -0.2914687 0.8863909 0.4351942 +v -0.3370509 0.7807804 0.4965856 +v -0.3089941 0.8174802 0.4857088 +v -0.3786229 0.9198930 0.4164187 +v -0.2928846 0.9323590 0.3869472 +v -0.3392226 0.8835306 0.4326590 +v -0.3686052 0.8302687 0.4764089 +v 0.5016189 0.5873753 0.2465551 +v 0.4474692 0.6705309 0.3809935 +v 0.4870872 0.5221568 0.3380627 +v 0.4235219 0.5647423 0.4449563 +v 0.4318939 0.6143899 0.4339961 +v 0.2751692 0.7703043 0.4888327 +v 0.1992644 0.7677454 0.5269405 +v 0.2333992 0.7243755 0.5518805 +v 0.2770336 0.6409693 0.5637692 +v 0.2038893 0.6245142 0.6053973 +v 0.3496453 0.7050053 0.4874672 +v 0.3186556 0.6781288 0.5171362 +v 0.3909982 0.6399186 0.4785471 +v 0.4901408 0.5853342 0.3143877 +v 0.4764907 0.6206598 0.3414457 +v 0.4821328 0.6600647 0.3117249 +v 0.4586499 0.5725443 0.3898917 +v -0.9142274 0.6458912 0.3092946 +v -0.6181711 1.3250715 0.2850502 +v -0.5556659 1.2782655 0.4454890 +v -0.5676715 1.2456254 0.4745998 +v -0.5333738 1.1986555 0.4275107 +v -0.5274607 1.2319435 0.3927599 +v 0.0054381 0.0331859 0.2024454 +v 0.6077570 0.1863435 0.2682705 +v 0.6248388 0.2330983 0.2829583 +v 0.6163048 0.2982649 0.3213549 +v 0.5763497 0.2187945 0.3419805 +v 0.2833200 0.0284630 0.2760586 +v 0.6527080 0.3511319 0.2452453 +v 0.6118667 0.3724903 0.2992646 +v 0.5369784 0.3615142 0.3329445 +v 0.5825337 0.3989169 0.2901038 +v -0.6577473 0.0520251 0.2752182 +v -0.3520953 0.0351455 0.2307845 +v -0.8864203 0.5390915 0.2556577 +v -0.7235857 1.3244294 0.3613050 +v -0.7760552 1.2929773 0.4381072 +v -0.0838064 0.0697982 0.2962692 +v -0.7901448 0.4342639 0.3555523 +v -0.7142349 0.3703318 0.3491889 +v -0.5853053 0.0336469 0.3082619 +v -0.3654011 0.0315387 0.3902970 +v -0.4531493 0.3671159 0.4987408 +v -0.4187863 0.4035676 0.5253564 +v -0.2254822 0.3165874 0.5174943 +v -0.2111444 0.4384550 0.6053613 +v -0.2597792 0.4093847 0.5021693 +v -0.2539797 0.3555833 0.4869080 +v -0.2668155 0.3056007 0.4684852 +v -0.3769207 0.2880113 0.5079991 +v -0.3144825 0.3223642 0.4888505 +v -0.3666570 0.3750420 0.5225721 +v -0.5067542 0.3347155 0.4386627 +v -0.5311386 0.2185021 0.3470268 +v -0.5281630 0.2631328 0.3553266 +v -0.5700719 0.2965380 0.3155941 +v -0.5259677 0.2664265 0.2944154 +v -0.9366567 0.9237458 0.3700570 +v -0.2134657 0.9531922 0.3709357 +v -0.9021074 0.8969541 0.4009446 +v -0.6508846 0.0642662 0.3370743 +v -0.6200305 0.1161899 0.3309346 +v -0.8672306 0.5893098 0.3766617 +v -0.3939152 0.9564165 0.3763641 +v -0.8356705 1.2461294 0.4762577 +v 0.4237323 0.7892263 0.3406012 +v 0.4672897 0.3781501 0.3493096 +v 0.4538506 0.3315386 0.3620032 +v 0.4320722 0.7114819 0.3798844 +v 0.3800316 0.7275281 0.4393684 +v -0.6173273 1.2591289 0.4960440 +v -0.6086285 1.1197312 0.4831158 +v -0.6073502 0.1500697 0.3685727 +v -0.6644201 0.1134283 0.4049902 +v 0.3793297 0.7663678 0.4190141 +v -0.4276564 0.8998085 0.4311602 +v -0.0680619 0.9754292 0.4130312 +v 0.0036776 0.9893665 0.4085875 +v -0.5351329 0.3201144 0.3863080 +v 0.3781457 0.0767588 0.3283924 +v 0.3437605 0.8459200 0.4098173 +v 0.3834534 0.8041749 0.3865769 +v -0.5146128 1.1208678 0.4030373 +v 0.1218471 0.9619240 0.4318355 +v -0.5562419 1.1115906 0.4554521 +v -0.5493309 1.0233805 0.5064261 +v -0.6187845 0.0369303 0.3290108 +v -0.5575147 0.0350999 0.3534306 +v -0.5430242 0.0371044 0.4071152 +v -0.5813928 1.1876900 0.4881533 +v -0.5613497 1.2158400 0.4506650 +v -0.6421074 0.0417056 0.3268587 +v -0.6714514 0.0462838 0.3867794 +v -0.5999761 0.0368280 0.3879584 +v 0.0503661 0.0481968 0.4460154 +v 0.0651366 0.0739121 0.4037657 +v 0.0855353 0.0710287 0.3601582 +v -0.4408518 0.3050198 0.4987398 +v -0.4170658 0.2485236 0.4999701 +v -0.3495937 0.1401405 0.5424216 +v -0.3567223 0.1677432 0.4956309 +v -0.3119603 0.1772687 0.5341520 +v -0.2890892 0.0849101 0.6285114 +v -0.2755694 0.0359871 0.6232569 +v -0.3162163 0.1235071 0.5962173 +v -0.3605957 0.0839903 0.5589713 +v -0.3177235 0.0355333 0.5820566 +v -0.2951188 0.0323907 0.5270789 +v -0.4997158 0.2850679 0.4392511 +v 0.4443364 0.2744571 0.3718992 +v -0.2225900 0.0708386 0.3627200 +v -0.2155282 0.0738653 0.4223272 +v -0.0987058 0.0711978 0.3674214 +v 0.5841284 0.2706965 0.3523099 +v -0.9203021 0.9218463 0.4102043 +v -0.9338657 0.9622869 0.4685671 +v -0.4679758 0.9170635 0.4188485 +v -0.7657117 0.4501999 0.4247519 +v 0.2209469 0.7987782 0.4918309 +v 0.2651313 0.8427312 0.4525125 +v 0.3297204 0.7560049 0.4652457 +v 0.2722100 0.9147391 0.4113029 +v -0.5180591 0.8937867 0.4561886 +v -0.6346310 0.3768104 0.4410723 +v -0.0575898 0.8807493 0.4999191 +v -0.2353214 0.7913879 0.5118998 +v -0.1605990 0.7967464 0.5231808 +v -0.1255908 0.7498524 0.5356784 +v -0.1938215 0.7560496 0.5390418 +v -0.2152406 0.7056578 0.5532636 +v -0.0195598 0.8878220 0.4942982 +v -0.0994009 0.8534544 0.5061576 +v 0.3721906 0.1544033 0.3776946 +v 0.3408341 0.1667109 0.4268665 +v 0.3491304 0.2082147 0.4480909 +v 0.2941003 0.1947426 0.4704026 +v 0.4023276 0.6779764 0.4456656 +v 0.1898440 0.8691108 0.4631276 +v 0.0830470 0.8722596 0.4992382 +v 0.0560287 0.9098051 0.4826989 +v 0.1163442 0.9035638 0.4700758 +v -0.0456717 0.9321496 0.4646476 +v 0.1047252 0.0611460 0.4059156 +v -0.5152513 0.8137558 0.5056680 +v -0.4410324 0.8534572 0.4718598 +v -0.4319638 0.7940058 0.5199940 +v -0.3929664 0.8630720 0.4613435 +v -0.5480790 0.8339002 0.4916777 +v -0.4907563 0.8576331 0.4712129 +v -0.8931544 1.1539088 0.4352057 +v -0.6514847 1.2244786 0.5337636 +v -0.5995123 1.0146972 0.5408217 +v -0.6585480 1.0946411 0.5866840 +v -0.6264006 1.0667229 0.5360600 +v -0.6857911 1.0680968 0.6259884 +v -0.7159188 1.1091220 0.6322083 +v -0.6950806 1.2328538 0.5438693 +v -0.6581540 1.1663066 0.5722240 +v -0.6170368 1.1735343 0.5103424 +v -0.5090639 0.0331091 0.3501706 +v -0.1198572 0.0749141 0.4707690 +v -0.1748366 0.0772719 0.4756307 +v 0.3122693 0.0994964 0.3943398 +v -0.1616247 0.0723123 0.3866137 +v -0.5237038 0.2128487 0.4132788 +v -0.7881417 0.8119426 0.4490054 +v -0.7377728 0.7496046 0.4967515 +v -0.7133444 0.7885268 0.4962280 +v -0.7747682 0.8138311 0.4686317 +v -0.7486635 0.7855869 0.4851311 +v -0.6871034 0.0594839 0.4186129 +v -0.1977052 0.8433528 0.4941884 +v -0.1944310 0.8868903 0.4593330 +v -0.1489413 0.9209489 0.4490183 +v -0.1102527 0.9232638 0.4621643 +v -0.1467650 0.8514279 0.4990970 +v 0.3489511 0.1240123 0.3712541 +v 0.3930362 0.3193174 0.4659152 +v 0.3488039 0.3163190 0.4907784 +v -0.4846487 0.9714207 0.4486319 +v -0.5900783 0.9200824 0.5253137 +v -0.5909569 0.9683912 0.5448301 +v -0.6198518 0.9270900 0.5922797 +v -0.6068485 0.8764345 0.4913145 +v -0.5473602 0.9180967 0.5012649 +v -0.6982121 1.0346851 0.6469928 +v -0.9260018 1.0037459 0.5271636 +v -0.5721271 1.0702878 0.4962147 +v -0.4080747 0.0339296 0.4372751 +v -0.5484040 0.1900705 0.4448623 +v -0.7748804 0.5965868 0.5020216 +v -0.7931116 0.5566244 0.4767703 +v -0.7292951 0.5707191 0.5225909 +v -0.7693810 0.7056801 0.4905571 +v -0.2572929 0.8242070 0.4838594 +v 0.1582199 0.8460593 0.4810166 +v 0.2849249 0.0626722 0.3776177 +v 0.2475061 0.0447348 0.4263029 +v 0.2721238 0.0790328 0.4582399 +v 0.2926435 0.1196054 0.4568909 +v 0.2459535 0.1718541 0.4893525 +v 0.2085378 0.1578834 0.5082026 +v 0.1623054 0.1702192 0.5539364 +v 0.1217266 0.1545832 0.5772156 +v 0.0906016 0.1390440 0.5723702 +v -0.7204412 0.4453015 0.4671864 +v -0.5221218 0.3622218 0.4604247 +v -0.4876271 0.4233684 0.5057060 +v -0.4578467 0.4571832 0.5298155 +v -0.4866625 0.4790619 0.5493194 +v -0.3218762 0.4992815 0.5204211 +v -0.3399165 0.4388811 0.5242525 +v -0.3635305 0.5077317 0.5387325 +v -0.4014116 0.4815719 0.5414710 +v -0.4093252 0.5496006 0.5322271 +v -0.4406040 0.5044169 0.5351013 +v -0.5258445 0.0370633 0.4770394 +v -0.5790898 0.3887148 0.4767092 +v -0.6397243 0.5468209 0.5602223 +v -0.6027850 0.4353432 0.5103215 +v 0.4150447 0.4432320 0.4546554 +v 0.1319149 0.0200834 0.4656244 +v 0.0223198 0.0266700 0.4840516 +v 0.3611074 0.4513458 0.5112008 +v 0.3039080 0.7324803 0.5010388 +v -0.2421709 0.0739124 0.4692568 +v -0.0827422 0.0478759 0.4960737 +v -0.6610264 0.4221959 0.4777615 +v -0.6783970 0.4813282 0.5098101 +v -0.6849359 0.5575625 0.5464128 +v -0.8718085 0.8719271 0.5754684 +v -0.9046334 0.9321176 0.5867892 +v -0.8929353 1.0217652 0.5770921 +v -0.8670737 1.0893414 0.5823258 +v -0.8462898 0.9387739 0.6271076 +v -0.8350937 0.8957424 0.6164183 +v -0.7907720 1.2056192 0.5569343 +v -0.2632680 0.1949541 0.5460985 +v -0.1305723 0.1458762 0.5982047 +v -0.1697320 0.2134399 0.5746254 +v -0.1322783 0.2306260 0.6036056 +v -0.2206017 0.2529493 0.5223429 +v -0.1816350 0.2638792 0.5767357 +v -0.2726151 0.2593477 0.4565300 +v -0.2076035 0.2062370 0.5358551 +v -0.2459479 0.1085312 0.6409912 +v -0.1943566 0.1120852 0.6367294 +v -0.1639655 0.0461738 0.6243995 +v -0.1401674 0.0277343 0.5799929 +v -0.2179745 0.1704546 0.6085797 +v -0.1615473 0.1864023 0.5660489 +v -0.2796926 0.2198769 0.4654272 +v -0.2433304 0.2204985 0.4926950 +v 0.3905935 0.3891517 0.4674919 +v 0.3676694 0.4196631 0.4871299 +v 0.3810501 0.5342072 0.4986088 +v 0.3380636 0.5843881 0.5234094 +v 0.2995262 0.5740820 0.5670088 +v 0.3884068 0.5871683 0.5006369 +v 0.3390125 0.5143764 0.5248256 +v 0.3853755 0.4905488 0.4962257 +v 0.2930556 0.4452532 0.5655643 +v -0.0863382 0.7917570 0.5346506 +v -0.4639558 0.2384619 0.4792361 +v 0.2740938 0.6922207 0.5477059 +v -0.3444451 0.6657702 0.5494407 +v -0.5685497 0.7771663 0.5316834 +v -0.3127966 0.2146209 0.4677139 +v 0.3180731 0.2506250 0.4956450 +v -0.3904696 0.6531377 0.5332763 +v -0.0470203 0.8061128 0.5361230 +v -0.3535173 0.3263289 0.5146598 +v -0.4086089 0.3390863 0.5187496 +v -0.2927035 0.3942336 0.4965048 +v -0.2969571 0.5475581 0.5207800 +v -0.2794487 0.4699501 0.5127792 +v -0.2550212 0.4939134 0.5709668 +v -0.2328205 0.5426018 0.6288900 +v -0.4510877 0.7331357 0.5345497 +v -0.0256037 0.7326602 0.5372936 +v -0.4401646 0.0617488 0.5516106 +v -0.4947866 0.0548994 0.5693810 +v 0.1467103 0.1313885 0.5275599 +v 0.2117423 0.1053920 0.5219007 +v 0.3518865 0.3641695 0.4932937 +v -0.6015863 0.6401546 0.5737131 +v -0.5121990 0.6389232 0.5582815 +v -0.5582677 0.6466019 0.5736566 +v -0.5860577 0.7048852 0.5485125 +v -0.2772985 0.7504894 0.5282269 +v -0.1921506 0.6777396 0.5576929 +v -0.2447636 0.6871683 0.5415646 +v -0.1507434 0.7045181 0.5458542 +v -0.3659813 0.2263582 0.4893306 +v -0.4263629 0.1765368 0.5011909 +v -0.5542027 0.5108002 0.5609258 +v -0.5721682 0.5857661 0.5614774 +v -0.5349129 0.5792878 0.5696902 +v -0.6422225 0.6974888 0.5435289 +v -0.5348099 0.4695763 0.5355166 +v -0.5070637 0.5309821 0.5575571 +v -0.6166906 0.1449831 0.5324830 +v -0.5692426 0.1313767 0.5653828 +v -0.5473669 0.0874173 0.5843982 +v -0.5896758 0.1779111 0.4931526 +v -0.4248028 0.1300834 0.5298561 +v 0.3206184 0.3072070 0.5184224 +v -0.6263431 0.4719663 0.5274146 +v -0.3627385 0.6149546 0.5440461 +v -0.6994158 0.6892307 0.5412590 +v -0.2351231 0.6514872 0.5558991 +v 0.2831459 0.2641520 0.5293726 +v -0.3584937 0.5550642 0.5516633 +v -0.6889728 0.6350873 0.5433074 +v 0.1175285 0.6958768 0.5791152 +v -0.7442772 0.8419793 0.5835868 +v -0.6917384 0.8488937 0.5827250 +v -0.6229109 0.9751072 0.6015539 +v -0.6472490 0.9284859 0.6450792 +v -0.7059271 0.9310141 0.6696277 +v -0.7800618 1.1711565 0.5961111 +v 0.1266485 0.0287239 0.5285476 +v 0.0852187 0.0825436 0.5424470 +v 0.1966666 0.0379262 0.5092113 +v -0.1779634 0.6603417 0.6094763 +v 0.0517585 0.0222837 0.5193294 +v 0.0634330 0.0393320 0.5461249 +v -0.5895922 0.0389273 0.5556661 +v -0.5409266 0.0386737 0.5592313 +v -0.6738436 0.0558960 0.4935005 +v -0.6247106 0.0852921 0.5515441 +v 0.2087221 0.3700592 0.6034638 +v -0.5929176 0.5355983 0.5494157 +v -0.4058088 0.5893841 0.5342431 +v 0.2177449 0.6688950 0.5672123 +v -0.5409873 0.1715323 0.5236251 +v -0.4966469 0.1298820 0.5508490 +v 0.2462488 0.2187628 0.5476133 +v -0.6268243 0.0541223 0.5501831 +v -0.6355549 0.0402504 0.5185391 +v -0.1412817 0.3469902 0.6328465 +v -0.0928800 0.2451053 0.6177297 +v -0.1429480 0.3115281 0.6268448 +v -0.1917900 0.3247748 0.5675209 +v 0.2458663 0.3320636 0.5630102 +v 0.2648729 0.3974824 0.5768169 +v 0.1762160 0.4377091 0.6350772 +v -0.0406971 0.0266627 0.5111895 +v -0.3530744 0.0392931 0.5550243 +v 0.2999378 0.5170014 0.5688288 +v -0.3884936 0.1012075 0.5204307 +v 0.1454201 0.0784924 0.5329498 +v -0.2430996 0.5815064 0.6006366 +v -0.7950594 0.9151013 0.6251299 +v -0.7532410 1.0470054 0.6685395 +v -0.8182840 0.9666323 0.6293423 +v -0.8053208 1.0267078 0.6626531 +v -0.8516245 1.0419139 0.6258398 +v -0.0009935 0.0984300 0.5639259 +v 0.0063423 0.0405235 0.5626628 +v -0.0155054 0.1376337 0.5588611 +v 0.0762552 0.1177069 0.5422006 +v 0.0255901 0.7088197 0.5951348 +v -0.4387271 0.0397677 0.5322885 +v -0.3272658 0.0629033 0.6049677 +v -0.1022183 0.3707359 0.6393716 +v -0.1728181 0.5838577 0.6663085 +v -0.2006266 0.6106409 0.6372002 +v -0.1999452 0.0310456 0.5524567 +v -0.1998222 0.0297279 0.6054904 +v -0.2573943 0.0323290 0.5471101 +v -0.2501481 0.1368364 0.6124226 +v -0.2620885 0.1636593 0.5972823 +v -0.1831478 0.1539612 0.6203544 +v -0.7734417 0.9746826 0.6515841 +v -0.7439846 1.0024039 0.6636686 +v -0.1842355 0.3750516 0.6117347 +v -0.1484879 0.4080659 0.6523900 +v -0.0285928 0.3783080 0.6613145 +v -0.0247820 0.4218742 0.6871100 +v 0.0254354 0.4546373 0.6899531 +v 0.0455104 0.3806576 0.6581558 +v -0.0257166 0.5046156 0.6781682 +v 0.0654804 0.4477825 0.6626068 +v 0.1479716 0.5027589 0.6405801 +v 0.0971560 0.5742722 0.6653119 +v 0.0956862 0.3904452 0.6542437 +v 0.1384705 0.4044362 0.6474202 +v 0.1231890 0.4593989 0.6589797 +v -0.1027714 0.4230435 0.6579086 +v -0.1299679 0.1069995 0.6110645 +v -0.0683788 0.1111421 0.5877653 +v -0.0570927 0.0431863 0.5848620 +v -0.1119081 0.0424284 0.6023768 +v -0.0885293 0.5572498 0.6787720 +v -0.0373274 0.6182181 0.6735344 +v -0.0468776 0.5694756 0.6771176 +v 0.2136157 0.4683632 0.6292063 +v 0.1137356 0.3227021 0.6520099 +v 0.1593648 0.3466820 0.6279960 +v -0.1591403 0.5142618 0.6820657 +v -0.1144177 0.5107390 0.6755801 +v -0.0851930 0.2905419 0.6457421 +v 0.0786523 0.2974212 0.6564492 +v 0.0893243 0.4880033 0.6554811 +v 0.0467669 0.5884240 0.6652775 +v 0.0054979 0.5633627 0.6744778 +v 0.0451704 0.5102261 0.6742003 +v -0.3603420 0.5095365 -0.1476361 +v -0.3807913 0.0446905 0.0911728 +v -0.4327417 0.0497949 0.2206751 +v -0.3956938 0.0490566 0.2504739 +v -0.4517587 0.0451008 0.2752122 +v -0.4817178 0.0452465 0.2271011 +v -0.4079798 0.0428488 0.3264798 +v -0.1343470 0.0536066 0.5050915 +v -0.3051529 0.0507610 0.4601837 +v -0.1830258 0.0515383 0.5151878 +# 2504 vertices, 0 vertices normals + +f 94 8 7 +f 130 129 135 +f 133 188 189 +f 168 190 70 +f 168 70 164 +f 2 164 69 +f 1 151 7 +f 8 6 81 +f 8 81 7 +f 9 81 95 +f 9 95 25 +f 130 11 10 +f 9 1 7 +f 8 94 6 +f 87 12 97 +f 101 125 102 +f 102 160 162 +f 163 192 15 +f 1227 21 17 +f 78 17 24 +f 78 24 77 +f 77 24 1271 +f 76 1271 23 +f 80 23 18 +f 21 20 17 +f 17 20 24 +f 23 19 18 +f 18 19 68 +f 182 22 24 +f 182 24 20 +f 1271 24 22 +f 169 26 170 +f 170 178 111 +f 111 109 171 +f 171 109 193 +f 171 193 172 +f 30 71 1607 +f 1613 1571 1614 +f 1614 1571 31 +f 31 1319 32 +f 1959 1399 35 +f 1742 492 36 +f 37 519 518 +f 1738 518 64 +f 1768 450 38 +f 51 431 49 +f 40 416 56 +f 41 2071 2058 +f 2058 2070 1423 +f 2157 2106 2105 +f 2011 2105 43 +f 44 2009 2104 +f 2122 1425 2012 +f 2012 1831 1423 +f 1423 52 53 +f 2058 54 41 +f 45 1768 46 +f 47 50 1915 +f 1916 50 39 +f 39 50 48 +f 49 1610 46 +f 1610 48 47 +f 1610 49 48 +f 47 48 50 +f 51 1768 38 +f 51 49 46 +f 39 370 1916 +f 56 421 41 +f 56 41 54 +f 40 56 55 +f 56 54 55 +f 1908 1935 55 +f 55 53 1908 +f 40 1936 371 +f 1936 1916 370 +f 56 416 421 +f 57 58 64 +f 64 58 1738 +f 1738 59 60 +f 60 59 61 +f 60 61 62 +f 62 61 63 +f 63 31 1744 +f 1744 31 32 +f 57 64 449 +f 45 57 449 +f 45 449 1768 +f 449 64 454 +f 71 107 72 +f 78 79 17 +f 29 107 30 +f 30 107 71 +f 1568 71 72 +f 1568 72 74 +f 66 1620 80 +f 1510 67 1470 +f 1470 67 1315 +f 1315 67 1317 +f 1317 67 1312 +f 69 164 68 +f 19 184 68 +f 68 184 69 +f 70 1511 68 +f 70 68 164 +f 1312 67 1511 +f 18 1509 80 +f 1509 18 1511 +f 72 73 74 +f 74 73 1576 +f 1576 79 75 +f 1271 76 77 +f 77 75 78 +f 78 75 79 +f 65 79 73 +f 73 79 1576 +f 1620 23 80 +f 23 1620 76 +f 76 1575 77 +f 77 1574 75 +f 75 1574 1576 +f 6 82 81 +f 6 93 82 +f 25 82 118 +f 82 93 118 +f 118 93 92 +f 82 95 81 +f 6 94 83 +f 83 94 84 +f 86 90 85 +f 127 114 91 +f 91 26 136 +f 94 12 84 +f 84 12 86 +f 86 12 88 +f 90 119 89 +f 90 88 13 +f 88 90 86 +f 90 89 85 +f 12 87 88 +f 6 96 93 +f 127 96 89 +f 136 118 91 +f 92 127 91 +f 91 118 92 +f 92 93 96 +f 92 96 127 +f 25 95 82 +f 6 83 96 +f 7 81 9 +f 1 9 11 +f 9 25 11 +f 84 86 85 +f 85 83 84 +f 89 96 85 +f 85 96 83 +f 97 94 151 +f 12 94 97 +f 87 97 120 +f 123 121 122 +f 98 13 120 +f 120 13 87 +f 122 14 98 +f 122 98 123 +f 98 120 123 +f 10 11 25 +f 88 87 13 +f 13 98 14 +f 99 14 115 +f 115 14 124 +f 112 101 102 +f 112 102 103 +f 103 104 195 +f 105 104 163 +f 105 163 108 +f 17 1228 1227 +f 1228 17 79 +f 73 72 106 +f 107 29 28 +f 107 28 108 +f 104 105 195 +f 103 195 187 +f 194 193 179 +f 179 193 177 +f 91 110 26 +f 170 110 178 +f 110 170 26 +f 179 113 100 +f 116 117 114 +f 101 112 179 +f 100 113 116 +f 100 116 115 +f 115 116 119 +f 126 89 119 +f 115 119 99 +f 126 116 114 +f 126 119 116 +f 179 100 101 +f 103 187 112 +f 117 178 110 +f 73 106 65 +f 107 108 106 +f 72 107 106 +f 124 100 115 +f 94 7 151 +f 25 118 10 +f 10 118 128 +f 99 119 90 +f 99 90 13 +f 99 13 14 +f 123 120 5 +f 123 157 121 +f 125 160 102 +f 158 122 121 +f 14 122 124 +f 125 101 124 +f 125 124 122 +f 127 126 114 +f 89 126 127 +f 128 118 136 +f 128 129 10 +f 10 129 130 +f 132 134 167 +f 134 161 167 +f 134 130 133 +f 150 135 137 +f 150 133 135 +f 133 150 188 +f 132 131 134 +f 131 130 134 +f 133 130 135 +f 161 133 189 +f 129 137 135 +f 128 136 137 +f 150 137 169 +f 145 111 171 +f 188 140 1139 +f 1253 142 145 +f 142 1253 144 +f 1253 145 143 +f 144 1317 1139 +f 139 143 145 +f 139 145 171 +f 189 188 141 +f 189 141 148 +f 140 188 147 +f 188 1139 141 +f 140 147 149 +f 138 149 147 +f 170 138 169 +f 149 146 145 +f 147 188 150 +f 150 169 138 +f 147 150 138 +f 137 129 128 +f 157 5 4 +f 4 152 157 +f 159 152 153 +f 153 154 181 +f 181 154 185 +f 185 191 155 +f 156 183 185 +f 185 183 186 +f 123 5 157 +f 159 157 152 +f 121 157 158 +f 122 158 159 +f 122 159 125 +f 157 159 158 +f 160 125 159 +f 153 181 159 +f 159 181 160 +f 132 1 131 +f 1 11 131 +f 151 1 165 +f 165 1 132 +f 167 161 168 +f 168 161 148 +f 166 132 167 +f 113 117 116 +f 117 110 114 +f 114 110 91 +f 100 124 101 +f 162 155 104 +f 163 104 155 +f 163 15 108 +f 15 16 108 +f 108 16 1227 +f 108 1227 106 +f 102 162 103 +f 5 151 165 +f 180 167 168 +f 148 190 168 +f 5 165 4 +f 4 2 3 +f 165 166 4 +f 4 166 2 +f 180 168 164 +f 180 164 2 +f 166 180 2 +f 166 167 180 +f 132 166 165 +f 137 136 169 +f 146 170 111 +f 146 111 145 +f 1257 174 1600 +f 1590 1256 1603 +f 1590 1603 1612 +f 1590 1612 1589 +f 175 1415 176 +f 1253 176 144 +f 149 170 146 +f 169 136 26 +f 134 133 161 +f 117 113 177 +f 117 177 178 +f 178 177 109 +f 178 109 111 +f 179 177 113 +f 152 4 3 +f 183 69 184 +f 184 19 22 +f 186 20 21 +f 22 182 184 +f 20 186 182 +f 186 183 182 +f 182 183 184 +f 152 3 156 +f 156 3 183 +f 183 3 69 +f 3 2 69 +f 162 160 181 +f 155 181 185 +f 154 153 152 +f 154 156 185 +f 191 185 186 +f 192 186 21 +f 156 154 152 +f 179 112 187 +f 179 187 194 +f 162 181 155 +f 148 141 1312 +f 148 1312 190 +f 70 190 1511 +f 1511 190 1312 +f 191 186 192 +f 192 21 15 +f 191 192 155 +f 155 192 163 +f 194 187 195 +f 193 109 177 +f 103 162 104 +f 961 196 240 +f 217 960 239 +f 1225 218 730 +f 715 232 231 +f 1512 231 1578 +f 757 227 228 +f 228 198 754 +f 200 213 1299 +f 1299 213 212 +f 1297 212 1261 +f 1556 1303 1183 +f 202 201 1182 +f 1582 1181 203 +f 956 203 1168 +f 275 1170 1175 +f 1229 1230 1208 +f 1126 1230 1169 +f 1126 1169 205 +f 260 208 209 +f 211 1184 1270 +f 969 1184 1283 +f 214 233 215 +f 215 229 1491 +f 1492 1491 224 +f 226 730 218 +f 218 197 242 +f 242 217 239 +f 1225 197 218 +f 217 242 197 +f 219 1490 1481 +f 1265 219 1264 +f 1490 219 214 +f 751 1632 199 +f 220 1265 200 +f 1783 221 222 +f 215 233 222 +f 752 223 221 +f 223 198 229 +f 225 244 216 +f 244 226 218 +f 980 225 216 +f 225 226 244 +f 980 224 225 +f 198 223 752 +f 198 230 1491 +f 1491 229 198 +f 198 228 227 +f 1578 231 227 +f 227 231 230 +f 230 224 1491 +f 224 230 231 +f 224 231 232 +f 980 1492 224 +f 222 223 229 +f 215 222 229 +f 233 214 1783 +f 219 751 214 +f 751 1783 214 +f 213 970 212 +f 210 1270 234 +f 256 984 988 +f 988 236 989 +f 237 250 1091 +f 1091 250 994 +f 298 248 291 +f 255 986 961 +f 196 961 251 +f 239 960 243 +f 244 218 242 +f 241 216 244 +f 238 244 242 +f 239 238 242 +f 196 251 245 +f 238 239 243 +f 196 245 243 +f 251 963 245 +f 238 1007 244 +f 990 251 986 +f 255 958 246 +f 246 1136 249 +f 297 995 248 +f 248 995 994 +f 249 237 246 +f 248 250 291 +f 247 291 249 +f 249 250 237 +f 291 250 249 +f 248 994 250 +f 961 986 251 +f 987 992 1001 +f 254 978 253 +f 1157 254 258 +f 258 254 235 +f 1005 235 965 +f 254 253 235 +f 235 256 258 +f 258 256 257 +f 1157 258 257 +f 1157 1129 254 +f 1185 259 260 +f 260 206 208 +f 259 1129 260 +f 957 261 262 +f 957 262 1761 +f 1761 262 1701 +f 269 1701 268 +f 204 1621 1642 +f 204 1642 261 +f 1621 274 1642 +f 1642 274 275 +f 1836 1767 1834 +f 268 267 266 +f 779 777 271 +f 777 270 271 +f 269 778 1701 +f 957 1761 1873 +f 954 1644 203 +f 269 270 778 +f 1701 778 1761 +f 1642 275 272 +f 275 952 272 +f 272 819 273 +f 1867 1840 267 +f 1840 266 267 +f 273 819 267 +f 276 819 952 +f 277 276 1624 +f 818 277 1763 +f 1278 279 1277 +f 1277 1187 1128 +f 281 1127 280 +f 808 1188 1124 +f 808 1124 813 +f 807 1004 1238 +f 1503 1239 282 +f 1269 323 1508 +f 333 1493 289 +f 291 247 290 +f 305 322 313 +f 292 298 290 +f 292 1220 296 +f 1192 1218 295 +f 296 1220 1192 +f 995 297 1164 +f 996 1238 1004 +f 296 298 292 +f 248 298 297 +f 296 297 298 +f 291 290 298 +f 299 289 1493 +f 299 1493 1268 +f 290 301 292 +f 293 299 1215 +f 1215 299 1268 +f 293 300 299 +f 299 300 289 +f 289 300 292 +f 289 292 301 +f 303 307 302 +f 313 304 302 +f 303 304 623 +f 623 304 314 +f 303 302 304 +f 1240 308 312 +f 1240 312 309 +f 1247 288 1248 +f 287 307 303 +f 312 307 287 +f 308 307 312 +f 312 288 309 +f 302 307 306 +f 305 313 306 +f 313 302 306 +f 623 314 605 +f 314 601 606 +f 606 601 612 +f 607 2208 609 +f 315 318 1996 +f 1965 1964 316 +f 316 317 1991 +f 1966 602 603 +f 321 320 294 +f 294 322 321 +f 1966 603 1965 +f 1965 603 325 +f 1964 325 324 +f 324 326 317 +f 1964 324 317 +f 1964 1965 325 +f 1597 327 1596 +f 476 343 344 +f 1286 1290 1289 +f 1525 339 1598 +f 1525 1598 340 +f 1596 322 330 +f 341 329 1267 +f 1267 332 341 +f 341 332 1524 +f 1524 332 334 +f 334 332 333 +f 333 335 334 +f 336 337 1288 +f 1288 337 338 +f 338 1291 1285 +f 1204 1291 1135 +f 1206 1135 1133 +f 330 331 329 +f 341 340 329 +f 340 341 1525 +f 344 339 1290 +f 343 600 344 +f 600 1599 344 +f 599 1597 342 +f 600 342 1599 +f 342 600 599 +f 599 326 1597 +f 1597 326 327 +f 328 317 326 +f 316 1992 472 +f 2208 345 346 +f 900 588 2453 +f 2364 2453 2454 +f 2364 2439 557 +f 2361 557 350 +f 2399 350 2395 +f 2420 2395 2356 +f 2294 2296 354 +f 1878 382 678 +f 1851 1749 385 +f 1851 365 366 +f 1914 366 1853 +f 358 1853 1950 +f 698 440 438 +f 1757 359 700 +f 502 360 456 +f 362 487 480 +f 735 364 484 +f 366 377 1951 +f 1951 406 407 +f 2017 375 367 +f 2050 371 369 +f 369 371 370 +f 370 371 1936 +f 1861 2050 369 +f 423 372 373 +f 386 427 367 +f 2103 406 377 +f 406 1951 377 +f 405 378 2121 +f 357 379 2038 +f 1878 379 382 +f 382 383 678 +f 377 366 380 +f 381 2121 378 +f 2121 382 379 +f 382 2121 381 +f 380 366 365 +f 380 365 381 +f 381 383 382 +f 385 383 381 +f 365 385 381 +f 385 384 383 +f 2103 378 376 +f 2149 376 405 +f 2149 405 356 +f 403 579 564 +f 387 2252 2255 +f 1371 2402 388 +f 2444 392 2320 +f 425 392 2443 +f 2443 2444 390 +f 420 419 391 +f 2202 410 409 +f 396 400 399 +f 399 400 387 +f 397 2255 1371 +f 388 397 1371 +f 395 2318 402 +f 412 2320 414 +f 414 2320 392 +f 2273 2202 402 +f 394 395 396 +f 402 2201 398 +f 398 396 395 +f 396 399 394 +f 394 399 397 +f 2319 2273 401 +f 402 401 2273 +f 2318 401 402 +f 395 402 398 +f 2255 397 387 +f 387 397 399 +f 580 403 564 +f 2149 565 403 +f 404 580 375 +f 403 580 404 +f 2103 377 380 +f 2103 380 378 +f 376 378 405 +f 406 404 407 +f 407 404 435 +f 2017 435 375 +f 404 406 403 +f 403 406 2103 +f 403 2103 2149 +f 435 404 375 +f 376 2149 2103 +f 386 400 427 +f 427 2146 408 +f 408 2146 2144 +f 408 2144 368 +f 2146 2201 2144 +f 373 1865 409 +f 2323 2407 418 +f 2244 2243 413 +f 411 417 2236 +f 372 423 415 +f 41 422 2071 +f 419 420 2323 +f 419 2323 418 +f 420 2151 2323 +f 2323 2151 414 +f 2151 417 414 +f 419 418 2243 +f 2151 2071 417 +f 422 41 421 +f 416 415 421 +f 421 415 422 +f 417 422 2236 +f 2236 415 423 +f 415 2236 422 +f 372 415 416 +f 372 416 371 +f 40 371 416 +f 412 414 417 +f 412 417 411 +f 424 2236 423 +f 373 424 423 +f 424 373 410 +f 2243 418 413 +f 413 418 2407 +f 414 426 2407 +f 392 426 414 +f 413 2407 425 +f 2443 2441 425 +f 426 425 2407 +f 425 426 392 +f 393 424 410 +f 374 409 1865 +f 371 2050 372 +f 48 49 428 +f 428 49 431 +f 429 38 450 +f 429 450 451 +f 428 431 446 +f 446 445 430 +f 445 431 443 +f 447 441 444 +f 447 444 433 +f 434 433 2016 +f 436 437 444 +f 1950 439 440 +f 440 358 1950 +f 436 440 437 +f 438 440 436 +f 437 440 439 +f 2016 444 437 +f 436 442 438 +f 441 359 442 +f 441 442 436 +f 1757 442 359 +f 700 359 443 +f 429 700 38 +f 448 38 443 +f 38 700 443 +f 441 432 443 +f 441 443 359 +f 444 441 436 +f 433 444 2016 +f 441 447 432 +f 432 445 443 +f 446 431 445 +f 430 445 447 +f 433 430 447 +f 430 433 1863 +f 433 434 1863 +f 431 51 448 +f 51 38 448 +f 449 450 1768 +f 450 449 455 +f 453 454 518 +f 518 454 64 +f 453 452 454 +f 452 449 454 +f 449 452 455 +f 451 450 455 +f 464 1670 584 +f 462 2261 2163 +f 1995 346 345 +f 584 495 464 +f 1882 469 465 +f 491 1636 488 +f 466 488 467 +f 361 511 1557 +f 361 1557 487 +f 487 466 480 +f 467 488 1636 +f 478 465 469 +f 2170 473 2227 +f 471 474 1770 +f 1770 474 475 +f 475 474 343 +f 475 343 476 +f 1869 328 474 +f 471 1869 474 +f 328 600 474 +f 474 600 343 +f 470 478 469 +f 344 1290 476 +f 476 1290 477 +f 477 1290 479 +f 482 1203 1200 +f 480 467 1200 +f 468 481 483 +f 482 483 1203 +f 482 468 483 +f 477 479 478 +f 481 468 479 +f 479 468 478 +f 479 1286 481 +f 481 1286 1287 +f 483 481 1300 +f 481 1287 1300 +f 470 476 477 +f 470 477 478 +f 466 467 480 +f 363 1310 364 +f 1201 485 197 +f 363 362 1309 +f 1309 362 480 +f 487 486 466 +f 466 486 1665 +f 488 466 1665 +f 1665 486 489 +f 490 1674 459 +f 486 504 489 +f 503 1674 490 +f 488 1665 491 +f 491 1671 465 +f 465 1671 1882 +f 492 35 493 +f 493 35 1399 +f 493 1399 513 +f 498 497 459 +f 1913 499 513 +f 459 499 498 +f 1913 498 499 +f 460 1854 500 +f 455 456 451 +f 360 451 456 +f 509 502 506 +f 509 506 510 +f 1557 1558 505 +f 487 505 486 +f 486 505 504 +f 504 505 501 +f 458 508 507 +f 458 507 514 +f 514 507 457 +f 502 456 457 +f 503 501 1674 +f 1674 501 508 +f 489 504 503 +f 504 501 503 +f 1674 458 459 +f 458 1674 508 +f 508 510 506 +f 506 507 508 +f 508 501 510 +f 457 507 502 +f 502 507 506 +f 1558 510 505 +f 501 505 510 +f 509 510 1558 +f 511 732 712 +f 1558 712 509 +f 456 455 512 +f 457 456 512 +f 455 452 512 +f 512 517 457 +f 512 452 515 +f 512 515 517 +f 457 517 514 +f 499 516 513 +f 499 458 514 +f 458 499 459 +f 515 452 453 +f 515 453 519 +f 515 519 493 +f 493 513 516 +f 515 493 516 +f 514 517 499 +f 499 517 516 +f 516 517 515 +f 37 36 519 +f 519 453 518 +f 36 492 519 +f 365 1851 385 +f 2315 520 2292 +f 522 357 524 +f 405 357 356 +f 356 357 522 +f 2125 2204 524 +f 522 524 521 +f 549 523 2126 +f 1903 527 747 +f 746 747 1754 +f 672 529 528 +f 533 531 2497 +f 2042 1890 2043 +f 2013 2043 1894 +f 2013 1894 1896 +f 532 2013 1896 +f 1999 532 530 +f 2500 2042 533 +f 2500 533 2497 +f 528 534 538 +f 1888 538 534 +f 543 2000 548 +f 544 2075 879 +f 546 879 2041 +f 538 1889 539 +f 539 1889 1905 +f 2000 547 548 +f 540 543 537 +f 537 543 1987 +f 1987 543 548 +f 542 547 546 +f 546 545 536 +f 546 547 545 +f 544 879 546 +f 1987 548 542 +f 542 548 547 +f 1888 534 535 +f 1888 535 540 +f 540 535 541 +f 535 1899 1898 +f 535 1898 541 +f 1899 535 534 +f 2306 2210 2304 +f 2055 550 551 +f 877 551 555 +f 555 2142 552 +f 553 2210 554 +f 553 554 549 +f 549 2126 553 +f 555 552 553 +f 553 525 555 +f 550 2142 551 +f 2143 550 2065 +f 2315 2292 2314 +f 2314 2292 554 +f 2482 587 588 +f 2453 588 591 +f 2453 591 2454 +f 558 350 557 +f 557 2439 559 +f 559 558 557 +f 2266 560 1383 +f 1670 2076 584 +f 2266 2203 560 +f 2085 2233 2232 +f 1376 1391 569 +f 562 581 2252 +f 2252 387 563 +f 2252 563 562 +f 564 562 580 +f 386 580 563 +f 563 387 386 +f 581 562 564 +f 2280 581 579 +f 566 579 565 +f 566 2280 579 +f 565 579 403 +f 566 568 2280 +f 2280 568 2253 +f 561 2396 2385 +f 561 2385 569 +f 568 2396 2253 +f 2253 2396 561 +f 569 2385 2375 +f 2353 571 572 +f 2232 573 2365 +f 569 2375 2353 +f 2353 2375 571 +f 575 2365 351 +f 577 351 2365 +f 572 577 573 +f 2232 2365 575 +f 574 575 351 +f 574 351 2356 +f 2397 576 559 +f 576 558 559 +f 2365 573 577 +f 571 570 572 +f 572 570 577 +f 577 2373 578 +f 351 577 578 +f 352 578 2373 +f 2373 577 570 +f 581 564 579 +f 562 563 580 +f 2261 2228 2163 +f 2163 2228 463 +f 2260 2261 462 +f 583 496 2078 +f 2076 496 584 +f 461 2145 585 +f 461 2260 462 +f 576 2376 586 +f 2080 2085 574 +f 2080 574 586 +f 587 592 588 +f 588 592 591 +f 2225 590 595 +f 2454 2411 556 +f 2411 592 2377 +f 591 592 2411 +f 587 2482 349 +f 598 348 347 +f 597 598 593 +f 595 593 598 +f 2449 2366 594 +f 2366 2449 597 +f 587 594 592 +f 598 347 595 +f 590 593 595 +f 348 598 596 +f 596 598 597 +f 596 597 2401 +f 2401 597 2449 +f 587 349 594 +f 326 599 328 +f 600 328 599 +f 320 327 325 +f 322 1596 321 +f 1596 327 321 +f 327 320 321 +f 327 324 325 +f 327 326 324 +f 319 318 612 +f 319 612 602 +f 612 601 602 +f 602 601 603 +f 603 1798 325 +f 2040 319 602 +f 318 319 1996 +f 318 315 604 +f 345 2208 604 +f 345 604 315 +f 605 314 606 +f 607 609 2068 +f 607 2068 2007 +f 604 2208 607 +f 607 2007 604 +f 609 2206 610 +f 610 608 609 +f 608 2167 609 +f 609 2167 2068 +f 609 2208 2206 +f 618 604 2007 +f 318 604 618 +f 318 618 612 +f 606 612 611 +f 618 611 612 +f 605 606 624 +f 286 619 2008 +f 614 613 2152 +f 615 1244 1988 +f 615 285 616 +f 2152 616 614 +f 614 285 617 +f 1988 285 615 +f 285 614 616 +f 2008 617 286 +f 311 286 617 +f 614 617 613 +f 619 606 611 +f 623 605 1750 +f 1750 605 624 +f 1750 621 1751 +f 1751 621 620 +f 1751 620 310 +f 310 620 311 +f 1750 624 621 +f 620 621 311 +f 311 621 286 +f 621 619 286 +f 621 624 619 +f 1976 625 2086 +f 1981 2088 1956 +f 908 627 907 +f 632 2449 349 +f 627 909 2095 +f 631 907 2095 +f 2095 2421 629 +f 629 2093 630 +f 631 2401 632 +f 2401 2449 632 +f 631 632 628 +f 628 632 901 +f 2401 629 630 +f 2401 630 596 +f 907 627 2095 +f 2089 2342 1954 +f 647 633 2431 +f 2290 646 912 +f 2290 911 635 +f 865 2479 2480 +f 868 2455 2457 +f 637 895 861 +f 2197 895 638 +f 2250 862 859 +f 2250 859 2199 +f 870 2182 2181 +f 640 2181 2229 +f 663 2229 641 +f 663 641 662 +f 667 2183 642 +f 662 852 660 +f 660 851 645 +f 2263 2219 2220 +f 2287 2424 2288 +f 648 649 634 +f 633 647 2393 +f 649 2431 634 +f 648 634 646 +f 2431 649 647 +f 1958 2221 2220 +f 1958 2220 2219 +f 2219 788 1958 +f 1958 788 650 +f 1715 823 658 +f 1953 1715 657 +f 656 801 1953 +f 1953 801 1715 +f 1715 658 657 +f 648 2424 649 +f 649 2398 647 +f 2288 648 2289 +f 2289 648 646 +f 659 2412 2309 +f 786 2283 645 +f 2308 663 661 +f 2284 664 661 +f 661 645 2284 +f 660 645 661 +f 664 2308 661 +f 660 661 663 +f 662 660 663 +f 640 663 2308 +f 663 640 2229 +f 2286 2263 2220 +f 664 2284 665 +f 2413 2446 2412 +f 2412 2446 666 +f 2412 666 2309 +f 2284 645 2283 +f 2286 2249 2263 +f 2283 792 2249 +f 667 642 781 +f 667 644 662 +f 667 662 641 +f 765 854 853 +f 1932 855 856 +f 762 1784 668 +f 1548 1527 1480 +f 200 670 220 +f 1842 1544 1543 +f 1752 1541 673 +f 1753 673 671 +f 671 673 1545 +f 1753 671 1842 +f 1842 672 539 +f 672 538 539 +f 1567 674 1632 +f 1632 674 199 +f 752 748 754 +f 754 748 753 +f 759 753 750 +f 759 750 758 +f 758 749 756 +f 756 749 1813 +f 741 1821 742 +f 738 1877 675 +f 680 675 681 +f 681 1747 682 +f 683 1595 693 +f 693 1595 1584 +f 693 1584 710 +f 691 710 690 +f 690 709 684 +f 687 688 707 +f 707 688 686 +f 689 690 684 +f 689 691 690 +f 691 693 710 +f 686 691 689 +f 682 683 1635 +f 682 1635 692 +f 680 692 738 +f 1747 695 682 +f 686 683 691 +f 687 756 1813 +f 684 685 689 +f 683 693 691 +f 737 1748 1852 +f 358 440 694 +f 696 694 1673 +f 696 1627 737 +f 697 1627 1628 +f 1595 683 695 +f 695 683 682 +f 697 695 1747 +f 1828 358 694 +f 737 1627 697 +f 1747 1748 697 +f 1748 737 697 +f 1628 1595 695 +f 694 440 698 +f 694 698 1673 +f 699 700 701 +f 451 360 701 +f 701 429 451 +f 700 429 701 +f 704 716 705 +f 684 1516 685 +f 685 706 708 +f 707 708 687 +f 756 687 708 +f 756 708 755 +f 755 708 706 +f 685 708 707 +f 707 689 685 +f 689 707 686 +f 1517 709 710 +f 736 712 713 +f 711 713 702 +f 702 713 703 +f 703 713 731 +f 703 731 716 +f 716 717 721 +f 721 727 1515 +f 1515 727 714 +f 704 703 716 +f 721 705 716 +f 714 727 718 +f 735 723 734 +f 733 720 717 +f 717 725 721 +f 721 725 727 +f 727 725 724 +f 718 724 722 +f 718 727 724 +f 719 718 722 +f 716 733 717 +f 723 724 720 +f 734 723 720 +f 722 724 723 +f 722 723 1224 +f 1224 723 735 +f 720 724 725 +f 725 717 720 +f 364 735 363 +f 363 734 726 +f 726 361 362 +f 361 487 362 +f 363 726 362 +f 714 718 728 +f 728 718 729 +f 716 731 733 +f 732 733 731 +f 713 732 731 +f 361 726 1518 +f 363 735 734 +f 734 720 1518 +f 734 1518 726 +f 733 1518 720 +f 1518 733 732 +f 711 736 713 +f 713 712 732 +f 1560 1531 699 +f 696 1534 1627 +f 1595 1628 1626 +f 1550 1528 1551 +f 680 738 675 +f 742 739 740 +f 738 692 739 +f 740 739 1635 +f 1635 739 692 +f 742 743 741 +f 743 742 740 +f 688 687 743 +f 688 743 740 +f 1813 741 743 +f 687 1813 743 +f 678 384 676 +f 1825 744 745 +f 1825 745 1826 +f 752 199 748 +f 1755 748 760 +f 1810 1755 1754 +f 746 1842 539 +f 1810 1754 747 +f 527 1903 878 +f 753 748 1811 +f 748 1755 1811 +f 750 749 758 +f 750 753 1811 +f 221 1783 751 +f 221 751 199 +f 752 754 198 +f 228 754 759 +f 759 758 755 +f 755 758 756 +f 759 755 757 +f 759 757 228 +f 706 757 755 +f 757 1578 227 +f 753 759 754 +f 221 199 752 +f 674 1752 760 +f 748 199 760 +f 760 199 674 +f 1752 674 1567 +f 1567 1632 1526 +f 670 200 1480 +f 764 1479 1548 +f 1548 1479 1547 +f 669 1293 763 +f 1547 1479 761 +f 1299 764 1480 +f 643 765 642 +f 765 853 766 +f 766 853 2109 +f 1631 767 1930 +f 642 765 766 +f 784 768 773 +f 769 770 771 +f 771 772 1841 +f 271 772 779 +f 774 780 775 +f 1761 778 776 +f 270 774 775 +f 778 270 775 +f 778 775 776 +f 777 768 774 +f 777 774 270 +f 777 779 773 +f 777 773 768 +f 768 780 774 +f 772 271 1841 +f 770 785 779 +f 768 782 780 +f 1631 775 780 +f 773 779 785 +f 767 782 766 +f 766 782 667 +f 766 667 781 +f 783 644 782 +f 768 784 783 +f 768 783 782 +f 784 785 852 +f 852 785 851 +f 2263 2166 795 +f 849 847 2061 +f 849 2061 2064 +f 2064 2059 850 +f 787 791 792 +f 2114 787 786 +f 2114 786 769 +f 793 769 786 +f 770 769 793 +f 771 2073 787 +f 771 787 2114 +f 848 2059 2073 +f 2073 2059 791 +f 791 2064 2060 +f 791 2060 797 +f 2063 794 795 +f 788 794 796 +f 796 2046 789 +f 792 791 797 +f 847 798 2061 +f 798 847 790 +f 798 790 2054 +f 2054 2062 798 +f 800 2112 2116 +f 2116 2112 2049 +f 2118 1715 801 +f 1715 2118 2116 +f 1713 802 803 +f 1641 806 815 +f 1587 951 278 +f 278 1523 814 +f 278 814 942 +f 941 810 943 +f 943 809 1501 +f 811 1128 812 +f 1158 1188 812 +f 812 1128 1158 +f 1523 279 1278 +f 1278 1128 810 +f 810 1128 811 +f 809 813 807 +f 807 1501 809 +f 1238 1252 807 +f 1004 807 813 +f 812 808 811 +f 943 811 809 +f 809 808 813 +f 809 811 808 +f 811 943 810 +f 942 814 810 +f 814 1278 810 +f 1523 278 951 +f 951 815 806 +f 1832 818 817 +f 1832 817 1766 +f 818 1839 277 +f 277 1839 819 +f 277 819 276 +f 818 1832 1839 +f 953 816 1762 +f 828 2047 821 +f 821 2047 2044 +f 821 2044 820 +f 2045 2054 2044 +f 263 846 849 +f 1835 263 264 +f 264 263 850 +f 850 848 264 +f 2054 790 2044 +f 2044 790 820 +f 821 843 842 +f 824 823 1714 +f 1714 2115 824 +f 824 825 823 +f 826 825 824 +f 825 826 822 +f 822 826 827 +f 828 842 827 +f 2115 2048 824 +f 824 2048 826 +f 828 826 829 +f 820 840 821 +f 828 821 842 +f 828 827 826 +f 2048 829 826 +f 820 790 840 +f 839 845 1859 +f 834 804 835 +f 1795 835 831 +f 831 1859 832 +f 1834 845 263 +f 1711 804 834 +f 804 830 835 +f 831 835 830 +f 831 830 836 +f 836 1859 831 +f 830 837 836 +f 840 843 821 +f 1846 837 830 +f 837 844 838 +f 837 838 839 +f 840 841 843 +f 843 841 839 +f 845 839 841 +f 837 839 1859 +f 838 842 843 +f 838 843 839 +f 838 844 842 +f 845 832 1859 +f 846 263 845 +f 846 845 840 +f 840 845 841 +f 790 847 846 +f 840 790 846 +f 849 2064 850 +f 850 263 849 +f 849 846 847 +f 850 2059 848 +f 651 650 789 +f 799 651 789 +f 794 788 795 +f 789 650 788 +f 2166 2263 792 +f 2249 792 2263 +f 792 2283 787 +f 786 645 793 +f 793 645 851 +f 851 660 852 +f 852 662 783 +f 783 662 644 +f 642 766 781 +f 667 782 644 +f 766 2109 767 +f 853 1932 2109 +f 643 854 765 +f 1931 2124 855 +f 1785 2051 2037 +f 859 857 855 +f 857 859 862 +f 857 862 863 +f 2199 859 2124 +f 639 2124 858 +f 639 858 854 +f 853 854 858 +f 853 858 1931 +f 2199 639 860 +f 2197 861 895 +f 863 2197 638 +f 2124 639 2199 +f 862 2197 863 +f 862 2250 2197 +f 864 869 2313 +f 2313 869 2434 +f 2445 2479 2446 +f 2446 2479 865 +f 2313 866 2502 +f 2502 866 867 +f 2502 2504 2248 +f 2248 868 2312 +f 2248 2504 868 +f 2455 2504 867 +f 2313 2434 866 +f 865 866 2434 +f 2480 2335 865 +f 865 2335 866 +f 866 2335 867 +f 2502 867 2504 +f 2455 867 2335 +f 2504 2455 868 +f 2312 868 872 +f 2309 871 2181 +f 2181 871 870 +f 643 870 871 +f 871 864 643 +f 864 871 869 +f 871 2309 869 +f 666 2434 2309 +f 2434 869 2309 +f 2194 2435 894 +f 893 2437 2367 +f 2367 2437 2392 +f 2392 2381 873 +f 2276 874 2251 +f 2251 2140 885 +f 885 2140 875 +f 880 875 876 +f 2041 879 878 +f 526 878 1934 +f 1934 2055 877 +f 1934 878 2055 +f 2055 551 877 +f 2065 878 2075 +f 2065 880 876 +f 880 2065 2075 +f 879 2075 878 +f 878 2065 2055 +f 885 875 882 +f 2251 885 2276 +f 2276 2391 2422 +f 873 874 2422 +f 2422 874 2276 +f 889 2416 2257 +f 883 2160 888 +f 883 888 2159 +f 888 886 2159 +f 2159 2148 883 +f 883 2148 2147 +f 884 544 882 +f 544 884 2075 +f 2075 884 880 +f 880 884 875 +f 2257 2416 887 +f 888 2160 887 +f 2159 886 885 +f 889 2426 2416 +f 2159 885 882 +f 2435 893 894 +f 2194 894 872 +f 637 2029 895 +f 2035 2275 892 +f 2303 891 2175 +f 2180 2175 891 +f 894 893 2275 +f 2450 881 2275 +f 2450 2275 893 +f 894 890 2503 +f 2503 890 636 +f 636 890 637 +f 637 890 2029 +f 896 2332 2189 +f 2187 2188 2354 +f 2354 2338 2330 +f 2131 2463 897 +f 897 2463 2132 +f 899 898 2487 +f 900 2488 2481 +f 901 2492 628 +f 628 2492 2472 +f 2472 2492 2494 +f 2494 2492 2493 +f 2476 902 903 +f 907 2472 904 +f 907 904 908 +f 906 2348 633 +f 2310 906 2341 +f 628 907 631 +f 908 905 627 +f 2094 2344 2343 +f 2094 2343 626 +f 2344 2094 909 +f 912 910 911 +f 912 911 2290 +f 912 913 910 +f 913 912 646 +f 914 918 2465 +f 2465 2452 914 +f 914 2452 2429 +f 914 2429 2489 +f 910 915 919 +f 919 911 910 +f 916 918 915 +f 916 915 910 +f 916 910 2490 +f 2479 2445 2478 +f 914 2489 918 +f 918 2489 923 +f 919 915 923 +f 923 915 918 +f 923 2489 2428 +f 2445 2447 2478 +f 921 2447 917 +f 917 920 922 +f 920 917 2447 +f 2447 921 2478 +f 911 919 635 +f 919 923 922 +f 923 2428 922 +f 919 922 920 +f 917 922 2428 +f 2089 1954 1955 +f 1242 1717 924 +f 284 924 927 +f 284 927 283 +f 924 284 1242 +f 1244 625 1242 +f 1242 625 1717 +f 924 948 949 +f 929 1645 932 +f 935 1587 278 +f 948 924 1717 +f 924 930 927 +f 283 927 933 +f 283 933 938 +f 283 938 925 +f 927 930 1664 +f 927 1664 933 +f 1483 934 1625 +f 1484 1633 935 +f 936 943 1501 +f 1484 936 1502 +f 1502 936 1501 +f 935 936 1484 +f 941 936 935 +f 1625 1484 1483 +f 934 1483 939 +f 925 938 940 +f 937 939 1483 +f 810 941 942 +f 942 941 935 +f 936 941 943 +f 1718 1719 945 +f 1719 1645 945 +f 944 945 946 +f 949 930 924 +f 928 930 949 +f 928 949 946 +f 928 946 929 +f 929 945 1645 +f 946 945 929 +f 1978 946 947 +f 1716 947 948 +f 947 1716 1978 +f 945 944 1718 +f 948 947 949 +f 949 947 946 +f 275 1175 952 +f 952 1175 276 +f 276 1175 1624 +f 953 1594 1237 +f 953 1237 816 +f 277 953 1763 +f 1624 1234 1594 +f 1237 1594 1234 +f 261 957 956 +f 956 957 954 +f 1629 954 955 +f 203 956 954 +f 1873 955 957 +f 957 955 954 +f 959 1136 246 +f 959 1132 1136 +f 246 958 959 +f 958 255 961 +f 958 961 959 +f 961 240 959 +f 960 240 243 +f 240 960 1132 +f 1132 959 240 +f 485 960 217 +f 1133 960 485 +f 197 485 217 +f 240 196 243 +f 990 236 251 +f 251 236 963 +f 1006 985 964 +f 976 985 965 +f 964 976 966 +f 966 983 964 +f 979 966 975 +f 981 967 1490 +f 974 973 968 +f 234 968 973 +f 1263 971 970 +f 970 1261 212 +f 1283 1261 971 +f 1283 971 969 +f 1270 1184 969 +f 1261 970 971 +f 971 968 969 +f 973 977 978 +f 967 972 977 +f 234 973 978 +f 967 973 974 +f 965 253 976 +f 967 981 972 +f 966 972 975 +f 253 978 977 +f 977 976 253 +f 977 972 966 +f 977 966 976 +f 981 975 972 +f 983 216 241 +f 216 983 979 +f 980 216 979 +f 979 975 982 +f 981 982 975 +f 980 979 982 +f 966 979 983 +f 241 964 983 +f 964 985 976 +f 963 1006 1007 +f 236 1006 963 +f 985 1006 984 +f 985 1005 965 +f 984 1005 985 +f 990 989 236 +f 986 255 962 +f 986 992 987 +f 989 987 1090 +f 987 1001 1090 +f 986 987 990 +f 990 987 989 +f 962 992 986 +f 238 243 245 +f 991 962 237 +f 991 237 1091 +f 246 962 255 +f 246 237 962 +f 999 993 1102 +f 995 1103 994 +f 991 1000 962 +f 994 1103 1091 +f 995 1164 1003 +f 1164 295 996 +f 1164 996 1003 +f 1103 995 997 +f 997 1154 1102 +f 1001 1002 1118 +f 252 1089 1118 +f 1102 1154 998 +f 1102 998 999 +f 1001 1118 1089 +f 999 1001 1000 +f 1000 993 999 +f 1001 992 1000 +f 962 1000 992 +f 1002 1001 999 +f 1124 1004 813 +f 1003 996 1004 +f 28 105 108 +f 1160 1159 194 +f 194 1159 172 +f 194 172 193 +f 1006 236 984 +f 984 236 988 +f 984 256 1005 +f 235 1005 256 +f 245 963 238 +f 963 1007 238 +f 1007 964 241 +f 244 1007 241 +f 1007 1006 964 +f 1008 1056 1009 +f 1032 1009 1033 +f 1032 1078 1191 +f 1191 1078 1079 +f 1010 1079 1077 +f 1473 1010 1354 +f 1011 1561 1562 +f 1012 1083 1281 +f 1012 1281 1013 +f 1358 1360 1014 +f 1342 1577 1015 +f 1017 1066 1759 +f 1759 1066 1801 +f 1018 1801 1046 +f 1046 1047 1334 +f 1334 1047 1019 +f 1640 1638 1020 +f 1020 1499 1566 +f 1022 1021 1023 +f 1022 1023 1111 +f 1111 1023 1024 +f 1111 1024 1113 +f 1113 1024 1025 +f 1027 1025 1026 +f 1027 1026 1106 +f 1106 1026 1140 +f 1106 1140 1100 +f 1100 1058 1028 +f 1028 1057 1029 +f 1028 1029 1030 +f 1035 1036 1097 +f 1097 1036 1031 +f 1096 1031 1095 +f 1009 1034 1008 +f 1032 1095 1009 +f 1009 1095 1034 +f 1031 1034 1095 +f 1031 1036 1034 +f 1035 1055 1036 +f 1036 1008 1034 +f 1705 1037 1069 +f 1039 1040 1282 +f 1282 1040 1552 +f 1705 1069 1042 +f 1042 1069 1677 +f 1677 1043 1067 +f 1067 1070 1044 +f 1801 1045 1046 +f 1047 1779 1693 +f 1693 1019 1047 +f 1067 1044 1017 +f 1017 1044 1066 +f 1066 1045 1801 +f 1069 1037 1053 +f 1069 1053 1073 +f 1038 1073 1048 +f 1323 1016 1049 +f 1450 1049 1054 +f 1054 1053 1037 +f 1052 1050 1274 +f 1052 1274 1048 +f 1274 1050 1039 +f 1048 1073 1052 +f 1073 1053 1052 +f 1054 1049 1050 +f 1053 1054 1050 +f 1053 1050 1052 +f 1073 1038 1072 +f 1008 1055 1056 +f 1036 1055 1008 +f 1057 1058 1059 +f 1059 1058 1140 +f 1272 1060 1061 +f 1279 1472 1282 +f 1282 1472 1039 +f 1478 1498 1063 +f 1063 1498 1500 +f 1070 1500 1065 +f 1038 1478 1072 +f 1072 1478 1064 +f 1064 1478 1063 +f 1064 1063 1071 +f 1071 1063 1070 +f 1045 1637 1638 +f 1045 1638 1640 +f 1045 1640 1639 +f 1046 1639 1047 +f 1677 1069 1043 +f 1043 1068 1067 +f 1046 1045 1639 +f 1045 1066 1637 +f 1637 1066 1044 +f 1637 1044 1065 +f 1065 1044 1070 +f 1072 1068 1043 +f 1068 1071 1070 +f 1071 1068 1072 +f 1071 1072 1064 +f 1069 1073 1043 +f 1063 1500 1070 +f 1055 1088 1056 +f 1009 1056 1074 +f 1078 1189 1359 +f 1033 1009 1074 +f 1033 1074 1084 +f 1078 1033 1189 +f 1079 1076 1077 +f 1079 1078 1076 +f 1056 1088 1086 +f 1056 1086 1074 +f 1080 1081 1082 +f 1082 1081 1085 +f 1082 1085 1060 +f 1082 1060 1272 +f 1080 1082 1276 +f 1084 1276 1275 +f 1084 1275 1083 +f 1012 1359 1189 +f 1084 1080 1276 +f 1083 1189 1084 +f 1080 1084 1074 +f 1087 1085 1086 +f 1074 1086 1081 +f 1074 1081 1080 +f 1086 1085 1081 +f 1085 1087 1060 +f 1060 1087 1059 +f 1088 1087 1086 +f 1088 1057 1087 +f 1087 1057 1059 +f 1055 1029 1088 +f 1088 1029 1057 +f 1090 988 989 +f 256 1089 257 +f 1090 1001 1089 +f 1157 257 252 +f 1089 252 257 +f 993 1000 991 +f 1030 1029 1035 +f 1035 1029 1055 +f 256 988 1090 +f 256 1090 1089 +f 1102 993 1091 +f 993 991 1091 +f 1115 1030 1101 +f 1101 1030 1035 +f 1163 1097 1092 +f 1109 1117 1487 +f 1566 1021 1094 +f 1565 1094 1093 +f 1021 1022 1094 +f 1094 1022 1093 +f 1163 1035 1097 +f 1095 1098 1096 +f 1010 1473 1099 +f 1097 1031 1096 +f 1010 1099 1098 +f 1098 1099 1096 +f 1096 1099 1092 +f 1092 1099 1364 +f 1092 1364 1162 +f 1100 1028 1104 +f 1104 1028 1030 +f 1104 1030 1115 +f 1101 1035 1163 +f 1092 1097 1096 +f 1131 999 998 +f 1102 1091 1103 +f 1102 1103 997 +f 1107 1104 1105 +f 1114 1027 1106 +f 1114 1106 1107 +f 1114 1107 1105 +f 1104 1107 1100 +f 1100 1107 1106 +f 1104 1115 1105 +f 1105 1115 1108 +f 1110 1022 1112 +f 1112 1022 1111 +f 1110 1112 1109 +f 1111 1113 1112 +f 1109 1112 1108 +f 1108 1112 1113 +f 1113 1114 1108 +f 1108 1114 1105 +f 1113 1025 1114 +f 1025 1027 1114 +f 1093 1022 1110 +f 1117 1109 1116 +f 1116 1109 1115 +f 1115 1109 1108 +f 1057 1028 1058 +f 253 965 235 +f 205 206 1119 +f 1122 1120 252 +f 1122 1118 1121 +f 1121 1118 1002 +f 1118 1122 252 +f 1119 206 1120 +f 1120 206 1130 +f 280 1123 281 +f 281 1123 1188 +f 1123 998 1188 +f 998 1154 1188 +f 1124 1003 1004 +f 1154 1124 1188 +f 280 1131 1123 +f 1125 280 1127 +f 1125 1126 1119 +f 1119 1122 1125 +f 1125 1121 280 +f 205 1119 1126 +f 1209 1176 1187 +f 1128 1187 1176 +f 1128 1176 1158 +f 1127 1176 1209 +f 1127 1209 1126 +f 252 1120 1157 +f 1157 1120 1130 +f 1129 1130 260 +f 1130 206 260 +f 1131 1002 999 +f 1123 1131 998 +f 1133 1132 960 +f 1151 1132 1133 +f 1132 1151 1136 +f 1136 1151 1134 +f 1138 1134 1152 +f 338 1153 1291 +f 249 1136 1138 +f 247 249 1138 +f 336 335 337 +f 1137 337 1266 +f 247 1137 1266 +f 337 1137 1153 +f 1152 1153 1137 +f 1152 1137 1138 +f 1138 1136 1134 +f 1059 1140 1144 +f 1140 1058 1100 +f 1140 1026 1144 +f 1144 1026 1141 +f 1143 1142 1150 +f 1148 1150 1024 +f 1060 1059 1144 +f 1292 1196 1476 +f 1476 1196 1145 +f 1145 1196 1195 +f 1495 1146 1021 +f 1021 1146 1023 +f 1146 1195 1148 +f 1147 1193 1141 +f 1141 1142 1143 +f 1141 1143 1147 +f 1147 1143 1148 +f 1148 1023 1146 +f 1024 1023 1148 +f 1148 1143 1150 +f 1026 1025 1149 +f 1026 1149 1142 +f 1026 1142 1141 +f 1142 1149 1150 +f 1024 1150 1025 +f 1025 1150 1149 +f 1122 1121 1125 +f 1131 1121 1002 +f 280 1121 1131 +f 1135 1151 1133 +f 1134 1151 1135 +f 997 1003 1154 +f 1154 1003 1124 +f 1156 1129 259 +f 1155 1156 259 +f 1270 210 211 +f 1185 210 1155 +f 1185 1155 259 +f 1156 254 1129 +f 997 995 1003 +f 195 27 1160 +f 194 195 1160 +f 149 142 140 +f 149 145 142 +f 142 144 140 +f 144 1139 140 +f 254 1156 978 +f 978 1156 1155 +f 978 1155 234 +f 1130 1129 1157 +f 281 1158 1176 +f 105 27 195 +f 1032 1191 1098 +f 1098 1095 1032 +f 1158 281 1188 +f 171 172 1258 +f 29 27 28 +f 27 29 1160 +f 1159 174 173 +f 1159 1160 174 +f 174 1160 1161 +f 174 1161 1600 +f 1601 30 1607 +f 1160 29 1161 +f 1161 29 30 +f 30 1601 1161 +f 1115 1101 1116 +f 1116 1101 1163 +f 1116 1163 1117 +f 1163 1092 1162 +f 1162 1117 1163 +f 1126 1125 1127 +f 1164 297 296 +f 105 28 27 +f 205 1169 207 +f 1167 1174 1168 +f 207 1169 1165 +f 1489 1171 1173 +f 1170 1171 1474 +f 1170 275 274 +f 1170 274 1171 +f 956 1168 1174 +f 956 1174 261 +f 261 1174 204 +f 1172 204 1174 +f 1173 1172 1174 +f 1167 1173 1174 +f 1171 1172 1173 +f 1166 1177 1165 +f 1177 207 1165 +f 1127 281 1176 +f 206 205 207 +f 206 207 208 +f 208 1197 209 +f 1178 1284 1179 +f 1182 1179 1180 +f 1181 1180 1168 +f 1489 1198 1166 +f 1181 1168 203 +f 1186 1183 211 +f 211 1183 1184 +f 1184 1183 1303 +f 1303 1283 1184 +f 1183 1178 1182 +f 1181 202 1182 +f 211 1185 1186 +f 1186 1185 209 +f 210 1185 211 +f 260 209 1185 +f 812 1188 808 +f 1078 1359 1075 +f 1076 1075 1522 +f 1189 1033 1084 +f 1192 295 296 +f 295 1164 296 +f 1144 1141 1193 +f 1061 1193 1292 +f 1292 1194 1196 +f 1194 1195 1196 +f 1148 1195 1194 +f 1148 1194 1147 +f 1193 1194 1292 +f 1193 1147 1194 +f 1193 1061 1060 +f 1193 1060 1144 +f 1197 1198 1488 +f 1488 1199 1284 +f 1197 1177 1198 +f 1198 1177 1166 +f 1198 1199 1488 +f 1179 1199 1180 +f 1199 1173 1167 +f 1180 1167 1168 +f 1199 1167 1180 +f 1489 1173 1198 +f 1198 1173 1199 +f 234 1155 210 +f 1197 208 207 +f 1177 1197 207 +f 1165 1169 1230 +f 1208 1230 1209 +f 1126 1209 1230 +f 485 1201 1206 +f 364 1202 484 +f 1202 1206 484 +f 484 1206 1201 +f 1204 1202 1307 +f 1285 1204 1307 +f 1285 1308 1205 +f 1308 1285 1307 +f 1285 1205 1302 +f 1302 1205 1203 +f 1200 1203 1205 +f 1134 1135 1152 +f 247 1138 1137 +f 173 1257 1255 +f 174 1257 173 +f 1159 173 172 +f 172 173 1258 +f 171 1258 139 +f 1098 1191 1079 +f 1207 1231 1474 +f 1207 1474 1166 +f 1474 1231 1170 +f 1175 1170 1231 +f 1208 1209 1210 +f 1210 1209 1187 +f 1235 1233 1211 +f 1210 1277 279 +f 279 950 1211 +f 1210 1187 1277 +f 1210 279 1233 +f 1211 1233 279 +f 1238 996 295 +f 1252 1218 1223 +f 1506 1504 1239 +f 1239 1504 1222 +f 1216 1217 308 +f 1216 1222 1217 +f 1217 1222 1212 +f 1219 300 1311 +f 1311 293 1507 +f 308 1213 307 +f 1214 1215 306 +f 307 1213 306 +f 306 1213 1214 +f 1507 1214 1213 +f 1215 1507 293 +f 1214 1507 1215 +f 300 293 1311 +f 308 1217 1213 +f 1192 1221 1218 +f 1212 1221 1219 +f 1219 1221 1220 +f 1192 1220 1221 +f 1212 1223 1221 +f 1221 1223 1218 +f 1201 1224 735 +f 1201 735 484 +f 1226 1225 1224 +f 1225 1226 197 +f 730 2495 729 +f 2495 715 729 +f 722 1224 719 +f 1201 1226 1224 +f 1226 1201 197 +f 106 1227 1228 +f 1228 65 106 +f 1296 1262 1305 +f 1305 1262 1298 +f 1229 1165 1230 +f 1165 1229 1166 +f 1229 1208 1236 +f 1232 1234 1175 +f 1229 1236 1207 +f 1207 1236 1232 +f 1231 1232 1175 +f 1232 1231 1207 +f 1236 1235 1232 +f 1236 1208 1233 +f 1234 1232 1235 +f 1235 1236 1233 +f 1235 1211 1234 +f 1237 1234 1211 +f 1237 1211 950 +f 1233 1208 1210 +f 1238 1218 1252 +f 1486 1223 1504 +f 1486 1504 1506 +f 1239 1240 282 +f 1241 1240 309 +f 308 1240 1216 +f 1240 1239 1216 +f 1241 1247 1249 +f 1988 1243 1245 +f 1242 1243 1244 +f 1244 1243 1988 +f 1243 1246 1245 +f 1242 1246 1243 +f 1751 310 1245 +f 1241 282 1240 +f 1241 309 1247 +f 1245 1246 1249 +f 1245 1248 1751 +f 1249 1247 1248 +f 1248 1245 1249 +f 1249 1251 926 +f 926 1251 925 +f 925 1251 283 +f 283 1250 284 +f 1251 1249 1246 +f 1250 1246 1242 +f 1250 1251 1246 +f 1251 1250 283 +f 284 1250 1242 +f 926 282 1241 +f 926 1241 1249 +f 1238 295 1218 +f 1486 1252 1223 +f 1255 1256 1590 +f 1253 1260 176 +f 1260 1588 176 +f 1256 1257 1600 +f 1255 1259 1258 +f 173 1255 1258 +f 1257 1256 1255 +f 1259 1588 1260 +f 139 1258 1259 +f 1259 1260 139 +f 1255 1254 1259 +f 1260 1253 143 +f 143 139 1260 +f 68 1511 18 +f 1297 1261 1262 +f 1262 1261 1283 +f 1262 1283 1298 +f 1297 1262 1296 +f 1297 1295 212 +f 1299 1295 764 +f 1295 1299 212 +f 1264 1263 1265 +f 970 213 1263 +f 213 200 1265 +f 213 1265 1263 +f 977 973 967 +f 247 1266 290 +f 1266 289 301 +f 337 335 1266 +f 1266 335 289 +f 289 335 333 +f 1267 1493 332 +f 1269 329 331 +f 329 1269 1267 +f 1493 1267 1268 +f 1269 331 323 +f 1268 1267 1269 +f 1266 301 290 +f 1270 968 234 +f 22 19 1271 +f 1271 19 23 +f 1272 1061 1273 +f 1272 1273 1471 +f 1471 1273 1313 +f 1272 1471 1472 +f 1189 1083 1012 +f 1275 1276 1279 +f 1277 1128 1278 +f 1278 814 1523 +f 2495 730 226 +f 226 225 2495 +f 1275 1281 1083 +f 1472 1279 1276 +f 1472 1276 1082 +f 1472 1082 1272 +f 1280 1281 1275 +f 1280 1275 1279 +f 1279 1282 1280 +f 1281 1051 1013 +f 1360 1051 1577 +f 1360 1013 1051 +f 1280 1282 1552 +f 1552 1041 1281 +f 1281 1041 1051 +f 1284 209 1197 +f 1229 1207 1166 +f 1220 292 1219 +f 1206 1202 1204 +f 1291 1204 1285 +f 338 1285 1302 +f 1288 1302 1301 +f 338 1302 1288 +f 1287 1286 1289 +f 1524 1301 1289 +f 1289 1301 1287 +f 1290 1286 479 +f 1135 1206 1204 +f 1291 1152 1135 +f 1292 1273 1061 +f 1273 1292 1313 +f 1298 1306 1305 +f 1296 1305 1293 +f 1297 1296 1294 +f 1297 1294 1295 +f 1293 761 1479 +f 1293 1479 1296 +f 1296 1479 1294 +f 1298 1283 1303 +f 1299 1480 200 +f 1186 209 1284 +f 1219 1311 1212 +f 1507 1212 1311 +f 1153 1152 1291 +f 1301 1300 1287 +f 1300 1302 1203 +f 1203 483 1300 +f 1301 1302 1300 +f 336 1475 334 +f 335 336 334 +f 1306 1303 1556 +f 1305 1306 1304 +f 1306 1298 1303 +f 1197 1488 1284 +f 1263 974 971 +f 971 974 968 +f 1501 807 1485 +f 1486 807 1252 +f 1225 730 1224 +f 1224 730 719 +f 1307 1202 1310 +f 1307 1310 1308 +f 1308 1309 1200 +f 1200 1205 1308 +f 1202 364 1310 +f 1310 1309 1308 +f 1309 1310 363 +f 480 1200 1309 +f 1219 292 300 +f 1314 1313 1048 +f 1314 1048 1274 +f 1415 1591 176 +f 176 1591 144 +f 1316 1591 1415 +f 141 1139 1317 +f 141 1317 1312 +f 1574 1409 1410 +f 1574 1410 1407 +f 1407 1422 1459 +f 1741 1431 1320 +f 1741 1320 1432 +f 1741 1432 33 +f 33 1432 1400 +f 1400 1432 1727 +f 1790 1321 1721 +f 1037 1322 1054 +f 1054 1322 1450 +f 1323 1435 1016 +f 1016 1435 1324 +f 1324 1435 1449 +f 1324 1437 1438 +f 1692 1439 1697 +f 1697 1440 1691 +f 1687 1429 1326 +f 1971 1428 1327 +f 1971 1327 1972 +f 1972 2104 2072 +f 1338 2108 1962 +f 1340 1398 1963 +f 1328 1923 1330 +f 1330 1923 1329 +f 1335 1330 1926 +f 1688 1332 1685 +f 1685 1332 1333 +f 1333 1844 1682 +f 1019 1844 1334 +f 1982 1336 1335 +f 1335 1336 1330 +f 1330 1336 1328 +f 1339 1397 1337 +f 2020 1337 1340 +f 2020 1340 1336 +f 1336 1340 1328 +f 1339 1337 2021 +f 1982 2020 1336 +f 2108 1338 1341 +f 1973 2105 1341 +f 1341 2105 2108 +f 1554 1324 1015 +f 1049 1450 1323 +f 1324 1438 1015 +f 1015 1438 1342 +f 1577 1342 1014 +f 1358 1325 1692 +f 1367 1694 1343 +f 1343 1681 1344 +f 1779 1640 1345 +f 1345 1640 1351 +f 1350 1347 1352 +f 1350 1352 1349 +f 1348 1367 1343 +f 1343 1344 1348 +f 1348 1344 1349 +f 1349 1344 1350 +f 1345 1347 1779 +f 1779 1347 1693 +f 1347 1345 1346 +f 1346 1345 1351 +f 1344 1693 1350 +f 1350 1693 1347 +f 1364 1353 1162 +f 1011 1564 1593 +f 1356 1355 1360 +f 1357 1356 1699 +f 1692 1700 1358 +f 1325 1358 1014 +f 1013 1355 1359 +f 1013 1359 1012 +f 1013 1360 1355 +f 1354 1364 1473 +f 1365 1353 1363 +f 1365 1363 1352 +f 1361 1367 1348 +f 1363 1361 1348 +f 1363 1348 1349 +f 1561 1592 1361 +f 1366 1592 1362 +f 1367 1361 1366 +f 1361 1592 1366 +f 1352 1363 1349 +f 1363 1364 1354 +f 1363 1354 1361 +f 1364 1363 1353 +f 1347 1346 1520 +f 1353 1365 1162 +f 1162 1521 1117 +f 1365 1521 1162 +f 1520 1352 1347 +f 1694 1367 1366 +f 33 1400 1855 +f 1855 1400 1368 +f 1368 1400 1401 +f 1369 1368 1735 +f 1369 1922 1906 +f 1928 1906 1959 +f 2272 2241 2441 +f 2242 2441 2241 +f 2272 2462 1395 +f 2406 2462 2461 +f 2406 2461 1390 +f 1390 2461 2440 +f 1390 2440 1375 +f 1375 389 2402 +f 1371 2254 1393 +f 1393 2254 1372 +f 1372 1391 1373 +f 1392 1391 1376 +f 1389 1375 2403 +f 1389 2403 1374 +f 1375 1389 1390 +f 1392 1393 1373 +f 1373 1393 1372 +f 1371 1394 2402 +f 2402 1394 2403 +f 2403 1394 1392 +f 1386 1376 2234 +f 1377 2271 2209 +f 1386 2267 2270 +f 1385 1379 1378 +f 2203 2231 2162 +f 1377 2209 2203 +f 1383 1384 1378 +f 1381 1385 1943 +f 1385 1384 1943 +f 1384 1382 1946 +f 560 1382 1384 +f 560 1384 1383 +f 1378 1384 1385 +f 1379 1381 1940 +f 1379 1385 1381 +f 1376 1386 2270 +f 1376 2270 1392 +f 1388 2269 2405 +f 2406 1390 1389 +f 2405 2406 1389 +f 1374 1388 1389 +f 2269 1388 1387 +f 1387 1392 2270 +f 1388 2405 1389 +f 569 1391 1372 +f 561 569 1372 +f 1393 1392 1394 +f 1393 1394 1371 +f 2272 1395 1370 +f 1368 1369 1856 +f 1856 1369 34 +f 34 1369 1906 +f 34 1906 1928 +f 1399 1959 1947 +f 1399 1947 1989 +f 1913 1989 498 +f 1397 1940 1939 +f 1990 1398 1941 +f 1941 1398 1397 +f 1913 513 1399 +f 1401 1400 1737 +f 1925 1729 1404 +f 1925 1404 1329 +f 1329 1404 1731 +f 1405 1926 1723 +f 1401 1735 1368 +f 1735 1402 1369 +f 1400 1727 1737 +f 1401 1737 1735 +f 1741 1740 1431 +f 1431 1740 1319 +f 1407 1410 1422 +f 1408 1410 1409 +f 1458 1422 1411 +f 1411 1421 1443 +f 1412 1421 1420 +f 1465 1412 1419 +f 1468 1829 1425 +f 1423 1831 52 +f 1606 52 1831 +f 1605 1424 175 +f 1605 175 1589 +f 1415 175 1424 +f 1649 1418 1416 +f 1650 1416 1420 +f 1650 1420 1758 +f 1409 1758 1408 +f 1415 1424 1805 +f 1417 1807 1808 +f 1413 1430 1418 +f 1649 1417 1808 +f 1430 1419 1418 +f 1416 1419 1412 +f 1416 1412 1420 +f 1420 1421 1758 +f 1758 1421 1408 +f 1419 1416 1418 +f 1408 1421 1411 +f 1408 1411 1410 +f 1410 1411 1422 +f 1805 1807 1417 +f 2012 1425 1831 +f 1606 1831 1424 +f 1908 52 1606 +f 1424 1605 1606 +f 1425 2122 1468 +f 1414 1829 1468 +f 1426 1469 2104 +f 1427 1426 1463 +f 1463 1426 1327 +f 1327 1428 1467 +f 1429 1428 1326 +f 1326 1428 1971 +f 2104 1469 44 +f 1327 1426 1972 +f 1426 2104 1972 +f 1413 1414 1469 +f 1413 1427 1430 +f 1430 1464 1419 +f 1412 1465 1460 +f 1412 1460 1443 +f 1458 1431 1459 +f 1459 1431 1319 +f 1727 1432 1433 +f 1433 1434 1321 +f 1321 1434 1778 +f 1321 1778 1451 +f 1321 1451 1721 +f 1721 1451 1322 +f 1439 1447 1440 +f 1324 1449 1437 +f 1440 1697 1439 +f 1436 1439 1325 +f 1439 1436 1447 +f 1691 1440 1467 +f 1443 1441 1442 +f 1441 1443 1444 +f 1455 1444 1446 +f 1455 1441 1444 +f 1443 1460 1444 +f 1444 1460 1461 +f 1461 1462 1445 +f 1447 1445 1466 +f 1447 1466 1440 +f 1440 1466 1467 +f 1445 1448 1446 +f 1445 1446 1461 +f 1444 1461 1446 +f 1447 1436 1445 +f 1445 1436 1448 +f 1446 1448 1437 +f 1446 1437 1454 +f 1454 1437 1449 +f 1323 1450 1451 +f 1451 1450 1322 +f 1451 1435 1323 +f 1451 1452 1435 +f 1435 1452 1449 +f 1446 1454 1455 +f 1452 1453 1449 +f 1449 1453 1454 +f 1451 1778 1452 +f 1455 1454 1453 +f 1452 1791 1453 +f 1452 1778 1791 +f 1455 1791 1457 +f 1455 1457 1441 +f 1441 1457 1442 +f 1455 1453 1791 +f 1321 1727 1433 +f 1432 1320 1792 +f 1442 1457 1456 +f 1442 1456 1458 +f 1458 1320 1431 +f 1792 1320 1456 +f 1320 1458 1456 +f 1443 1442 1411 +f 1411 1442 1458 +f 1422 1458 1459 +f 1443 1421 1412 +f 1413 1469 1426 +f 1413 1426 1427 +f 1460 1465 1461 +f 1427 1464 1430 +f 1464 1463 1466 +f 1419 1464 1462 +f 1419 1462 1465 +f 1461 1465 1462 +f 1466 1445 1462 +f 1466 1462 1464 +f 1463 1464 1427 +f 1327 1466 1463 +f 1466 1327 1467 +f 1468 1469 1414 +f 1468 44 1469 +f 1313 1314 1471 +f 1314 1274 1471 +f 1471 1274 1472 +f 1039 1472 1274 +f 1364 1099 1473 +f 1556 1555 1306 +f 1306 1555 1304 +f 1183 1186 1178 +f 1186 1284 1178 +f 1178 1179 1182 +f 1474 1171 1166 +f 1485 807 1486 +f 1475 1288 1301 +f 1288 1475 336 +f 1301 1524 1475 +f 1524 334 1475 +f 1476 1477 1038 +f 1038 1477 1478 +f 1038 1048 1313 +f 1476 1313 1292 +f 1313 1476 1038 +f 1480 764 1548 +f 967 974 1481 +f 967 1481 1490 +f 1483 1482 937 +f 1484 1502 1483 +f 1483 1502 1482 +f 1504 1223 1212 +f 1493 333 332 +f 1117 1521 1487 +f 1110 1487 1519 +f 1110 1109 1487 +f 201 1183 1182 +f 201 1556 1183 +f 1179 1284 1199 +f 1489 1166 1171 +f 974 1263 1481 +f 1264 1481 1263 +f 980 982 1492 +f 215 981 1490 +f 1490 214 215 +f 981 215 1492 +f 1491 1492 215 +f 982 981 1492 +f 1476 1145 1494 +f 1494 1145 1495 +f 1500 1498 1497 +f 1500 1497 1496 +f 1495 1497 1494 +f 1476 1494 1477 +f 1494 1497 1477 +f 1478 1477 1498 +f 1498 1477 1497 +f 1495 1496 1497 +f 1145 1195 1495 +f 1566 1499 1021 +f 1499 1495 1021 +f 1146 1495 1195 +f 1500 1496 1065 +f 1062 1065 1496 +f 1495 1499 1496 +f 1496 1499 1062 +f 1499 1020 1062 +f 1062 1020 1638 +f 1190 1010 1077 +f 1190 1077 1522 +f 1190 1561 1010 +f 1354 1010 1561 +f 1501 1485 1502 +f 1482 1502 1485 +f 1482 1485 1505 +f 937 1505 1503 +f 937 1503 938 +f 1505 937 1482 +f 719 730 729 +f 719 729 718 +f 1222 1504 1212 +f 1216 1239 1222 +f 1522 1077 1076 +f 1305 1304 1293 +f 1293 1304 763 +f 1505 1506 1503 +f 1485 1506 1505 +f 1503 1506 1239 +f 1506 1485 1486 +f 1213 1217 1507 +f 1507 1217 1212 +f 1508 1268 1269 +f 1215 1268 1508 +f 1215 1508 306 +f 1508 323 306 +f 306 323 305 +f 305 323 322 +f 313 294 304 +f 322 294 313 +f 1510 1509 67 +f 1511 67 1509 +f 80 1509 66 +f 66 1509 1510 +f 935 278 942 +f 232 225 224 +f 225 232 2495 +f 1513 728 715 +f 1512 715 231 +f 1512 1513 715 +f 721 1515 705 +f 709 1515 684 +f 1516 714 728 +f 684 1515 1516 +f 728 1513 1516 +f 714 1516 1515 +f 1516 1513 1514 +f 685 1516 1514 +f 709 1517 705 +f 1515 709 705 +f 705 1517 704 +f 361 1518 732 +f 732 511 361 +f 1352 1520 1521 +f 1352 1521 1365 +f 1519 1520 1346 +f 1519 1346 1351 +f 1487 1520 1519 +f 1093 1110 1519 +f 1521 1520 1487 +f 1075 1359 1355 +f 1522 1563 1562 +f 1355 1563 1522 +f 1522 1562 1190 +f 1190 1562 1561 +f 950 279 1523 +f 950 1523 951 +f 728 729 715 +f 704 1517 1551 +f 1538 704 1551 +f 704 1538 703 +f 1524 1525 341 +f 144 1591 1315 +f 1317 144 1315 +f 1075 1355 1522 +f 670 1526 220 +f 670 1480 1527 +f 1567 1526 1527 +f 1757 1532 698 +f 1673 698 1532 +f 1530 1529 1532 +f 1536 1529 1533 +f 1533 1528 1550 +f 1550 1537 1535 +f 1550 1535 1533 +f 1534 1536 1535 +f 1534 1532 1536 +f 1533 1535 1536 +f 1537 1626 1535 +f 1535 1626 1534 +f 1538 1551 1528 +f 702 1538 1533 +f 1540 1533 1529 +f 1538 1528 1533 +f 703 1538 702 +f 702 1540 1539 +f 1539 1540 1530 +f 1539 1530 1560 +f 1560 1530 1531 +f 711 702 1539 +f 1533 1540 702 +f 1540 1529 1530 +f 1527 1549 1541 +f 1541 1549 1545 +f 1545 1549 1546 +f 1545 1546 1780 +f 1780 1782 1542 +f 671 1544 1842 +f 1543 1542 529 +f 672 1543 529 +f 1893 529 1542 +f 1546 1549 1547 +f 1656 1547 761 +f 1549 1527 1548 +f 1547 1549 1548 +f 1527 1526 670 +f 709 690 710 +f 1040 1039 1050 +f 1552 1040 1553 +f 1553 1016 1554 +f 1051 1554 1015 +f 1051 1015 1577 +f 1016 1553 1049 +f 1040 1050 1049 +f 1049 1553 1040 +f 1554 1041 1553 +f 1554 1051 1041 +f 1553 1041 1552 +f 1582 1555 201 +f 1555 1556 201 +f 1481 1264 219 +f 1557 505 487 +f 1558 1557 511 +f 1558 511 712 +f 712 736 509 +f 509 736 1559 +f 502 509 1559 +f 736 711 1559 +f 1539 1559 711 +f 1559 1539 1560 +f 1560 360 502 +f 1524 1289 1525 +f 339 1525 1289 +f 1361 1354 1561 +f 1564 1011 1562 +f 1564 1562 1563 +f 1356 1563 1355 +f 1593 1564 1357 +f 1564 1563 1357 +f 1357 1563 1356 +f 1011 1592 1561 +f 220 1526 1632 +f 220 1632 1265 +f 1200 467 1636 +f 1636 482 1200 +f 1565 1519 1351 +f 1519 1565 1093 +f 1094 1565 1566 +f 1566 1565 1020 +f 1565 1351 1020 +f 1351 1640 1020 +f 201 202 1582 +f 1181 1582 202 +f 204 1172 1621 +f 1621 1172 274 +f 1541 1567 1527 +f 1567 1541 1752 +f 673 1541 1545 +f 331 330 323 +f 1569 1568 1572 +f 1572 1568 1573 +f 1573 1568 74 +f 71 1568 1607 +f 1607 1568 1569 +f 1569 1572 1613 +f 1613 1572 1570 +f 1613 1570 1571 +f 31 1571 1459 +f 31 1459 1319 +f 1459 1571 1407 +f 32 1319 1740 +f 1570 1407 1571 +f 1573 1576 1574 +f 1575 1409 1574 +f 1574 1407 1573 +f 1573 1407 1570 +f 1573 1570 1572 +f 1409 1575 1672 +f 1573 74 1576 +f 1574 77 1575 +f 1014 1360 1577 +f 706 1578 757 +f 1578 706 1514 +f 1578 1514 1512 +f 1551 710 1584 +f 1550 1584 1537 +f 1584 1550 1551 +f 1583 1579 1581 +f 1581 1929 1580 +f 1582 1579 1583 +f 1581 1580 1622 +f 668 1793 762 +f 1643 1582 203 +f 1643 203 1644 +f 1623 1583 1793 +f 1624 1175 1234 +f 685 1514 706 +f 1256 1600 1603 +f 806 950 951 +f 834 1795 805 +f 1587 935 1633 +f 1585 1587 1645 +f 951 1587 815 +f 1585 815 1587 +f 1584 1595 1537 +f 1626 1537 1595 +f 175 1588 1589 +f 175 176 1588 +f 1588 1259 1254 +f 1588 1254 1589 +f 1255 1590 1254 +f 1589 1254 1590 +f 1315 1591 1316 +f 1362 1592 1653 +f 1592 1011 1653 +f 1653 1011 1593 +f 816 1237 950 +f 816 950 806 +f 1594 953 277 +f 926 940 282 +f 282 940 1503 +f 1503 940 938 +f 330 329 342 +f 342 1597 330 +f 330 1597 1596 +f 1599 342 1598 +f 344 1599 339 +f 329 340 342 +f 1161 1601 1600 +f 1600 1601 1602 +f 1605 1589 1612 +f 1604 1600 1602 +f 1915 1907 1775 +f 1907 1606 1605 +f 50 1916 1915 +f 1608 1607 1616 +f 1608 1616 1617 +f 1608 1617 1611 +f 1611 1617 1619 +f 1611 1619 1609 +f 1776 1610 1800 +f 1800 1610 47 +f 46 1610 1776 +f 1607 1608 1601 +f 1601 1608 1602 +f 1611 1777 1608 +f 46 1776 1609 +f 1609 1776 1611 +f 1605 1612 1907 +f 1612 1603 1604 +f 1604 1602 1777 +f 1777 1602 1608 +f 1616 1607 1569 +f 1618 1616 1614 +f 1618 1614 61 +f 1618 61 59 +f 1618 59 58 +f 58 59 1738 +f 1616 1569 1613 +f 1616 1613 1614 +f 1615 61 1614 +f 61 1615 63 +f 1614 31 1615 +f 31 63 1615 +f 1616 1618 1617 +f 1617 58 1619 +f 1619 58 57 +f 58 1617 1618 +f 76 1620 1672 +f 66 1651 1620 +f 1651 1672 1620 +f 66 1510 1318 +f 1356 1358 1699 +f 1360 1358 1356 +f 1304 1555 1623 +f 1583 1555 1582 +f 1633 1484 1625 +f 931 1625 934 +f 686 1634 683 +f 925 940 926 +f 937 938 933 +f 933 939 937 +f 340 1598 342 +f 1575 76 1672 +f 1583 1622 1793 +f 1581 1622 1583 +f 763 1304 1623 +f 1623 668 763 +f 1793 668 1623 +f 1623 1555 1583 +f 1624 1594 277 +f 932 1625 931 +f 1633 1625 932 +f 1626 1628 1534 +f 1628 1627 1534 +f 482 1636 468 +f 1290 339 1289 +f 1598 339 1599 +f 323 330 322 +f 1679 1643 1629 +f 1788 1630 1875 +f 1875 1630 1631 +f 1265 1632 219 +f 219 1632 751 +f 1634 686 688 +f 1634 740 1635 +f 1635 683 1634 +f 740 1634 688 +f 502 1559 1560 +f 468 491 465 +f 465 478 468 +f 1637 1065 1062 +f 1637 1062 1638 +f 1639 1640 1779 +f 815 1585 1641 +f 1641 1585 1586 +f 806 1641 1765 +f 1641 1586 805 +f 805 1586 834 +f 1641 805 1765 +f 1587 1633 1645 +f 932 1645 1633 +f 695 697 1628 +f 1600 1604 1603 +f 230 198 227 +f 1536 1532 1529 +f 1636 491 468 +f 261 1642 262 +f 1643 1579 1582 +f 1644 1629 1643 +f 1629 955 1788 +f 955 1873 1788 +f 954 1629 1644 +f 1532 1531 1530 +f 1560 701 360 +f 1470 1315 1646 +f 1510 1647 1318 +f 66 1648 1651 +f 1648 1318 1647 +f 1648 66 1318 +f 1646 1417 1647 +f 1470 1647 1510 +f 1470 1646 1647 +f 1647 1649 1648 +f 1649 1647 1417 +f 1416 1648 1649 +f 1648 1650 1651 +f 1648 1416 1650 +f 1646 1315 1316 +f 1325 1438 1436 +f 1438 1325 1342 +f 1436 1438 1448 +f 1700 1692 1652 +f 1698 1696 1362 +f 1698 1653 1654 +f 1654 1653 1593 +f 1698 1362 1653 +f 1325 1014 1342 +f 761 669 1657 +f 1657 1659 1655 +f 1546 1547 1656 +f 1656 761 1655 +f 1655 761 1657 +f 1546 1656 1781 +f 1658 1656 1659 +f 532 1658 530 +f 1896 1781 532 +f 1658 532 1781 +f 1659 1661 1658 +f 1658 1661 530 +f 1661 2496 530 +f 1656 1658 1781 +f 1656 1655 1659 +f 1660 1784 1787 +f 1787 1785 2496 +f 2037 2496 1785 +f 1659 1657 1660 +f 1657 669 1662 +f 1663 1809 1580 +f 856 1663 1580 +f 680 681 692 +f 681 682 692 +f 934 939 1664 +f 1664 931 934 +f 928 931 1664 +f 933 1664 939 +f 928 1664 930 +f 701 1560 699 +f 491 1665 1666 +f 1669 1667 500 +f 500 1667 460 +f 460 1667 1668 +f 489 1667 1666 +f 1666 1667 1669 +f 490 1668 503 +f 1671 1669 464 +f 1671 464 1882 +f 1882 464 1884 +f 464 495 1884 +f 464 1669 1670 +f 500 1670 1669 +f 491 1666 1671 +f 1409 1672 1651 +f 669 763 668 +f 1673 1532 1534 +f 1673 1534 696 +f 287 288 312 +f 1705 1678 1675 +f 1675 1678 1707 +f 1708 1707 1406 +f 1708 1406 1676 +f 1708 1676 1709 +f 1676 1802 1709 +f 1677 1067 1406 +f 1406 1707 1677 +f 1707 1678 1677 +f 1677 1678 1042 +f 1042 1678 1705 +f 1322 1037 1721 +f 1788 1680 1629 +f 1629 1680 1679 +f 1581 1876 1929 +f 1933 1874 1930 +f 1645 1719 1585 +f 1531 1532 1757 +f 1344 1681 1693 +f 1019 1693 1681 +f 1019 1681 1682 +f 1019 1682 1844 +f 1681 1343 1683 +f 1343 1695 1683 +f 1684 1682 1683 +f 1683 1695 1684 +f 1333 1682 1684 +f 1333 1684 1685 +f 1690 1691 1686 +f 1686 1691 1429 +f 1686 1429 1687 +f 1688 1686 1687 +f 1688 1948 1949 +f 1331 1332 1949 +f 1949 1332 1688 +f 1691 1690 1697 +f 1692 1697 1652 +f 1689 1686 1688 +f 1689 1688 1685 +f 1689 1685 1684 +f 1686 1689 1858 +f 1686 1858 1690 +f 1683 1682 1681 +f 1695 1343 1694 +f 1684 1695 1689 +f 1689 1695 1858 +f 1858 1695 1696 +f 1696 1698 1697 +f 1696 1697 1690 +f 1652 1697 1698 +f 1362 1696 1694 +f 1362 1694 1366 +f 1694 1696 1695 +f 1654 1652 1698 +f 1654 1700 1652 +f 1593 1357 1699 +f 1593 1699 1654 +f 1700 1654 1699 +f 1358 1700 1699 +f 1701 273 268 +f 623 622 303 +f 303 622 287 +f 1721 1037 1706 +f 1706 1702 1725 +f 1703 1704 1723 +f 1723 1704 1405 +f 1706 1037 1705 +f 1706 1705 1675 +f 1706 1675 1707 +f 1707 1708 1702 +f 1702 1708 1724 +f 1707 1702 1706 +f 1708 1709 1724 +f 1709 1802 1703 +f 1802 1704 1703 +f 1585 1719 1586 +f 834 1710 1711 +f 1713 657 658 +f 803 1952 1713 +f 802 1713 823 +f 2116 2115 1715 +f 2115 1714 1715 +f 823 1715 1714 +f 823 1713 658 +f 803 1712 1952 +f 1921 944 2002 +f 2002 944 1978 +f 1977 1978 1716 +f 1716 948 1717 +f 944 946 1978 +f 944 1921 1718 +f 1718 1921 1720 +f 1718 1710 1719 +f 1713 1952 657 +f 1843 804 1711 +f 1710 834 1586 +f 1710 1586 1719 +f 1721 1706 1790 +f 1725 1702 1728 +f 1723 1722 1703 +f 1702 1724 1728 +f 1728 1724 1733 +f 1733 1709 1722 +f 1724 1709 1733 +f 1790 1706 1725 +f 1727 1321 1726 +f 1734 1725 1728 +f 1734 1728 1733 +f 1734 1733 1403 +f 1403 1922 1402 +f 1403 1732 1730 +f 1731 1730 1937 +f 1729 1731 1404 +f 1731 1729 1730 +f 1730 1729 1403 +f 1937 1732 1722 +f 1722 1723 1937 +f 1732 1937 1730 +f 1732 1403 1733 +f 1725 1734 1726 +f 1726 1734 1736 +f 1735 1736 1402 +f 1402 1736 1403 +f 1403 1736 1734 +f 1727 1726 1737 +f 1726 1736 1735 +f 1726 1735 1737 +f 309 288 1247 +f 518 1738 60 +f 518 60 37 +f 1739 32 1740 +f 1740 1741 1746 +f 1740 1746 1739 +f 1743 1745 1744 +f 1744 32 1743 +f 1743 36 1745 +f 36 1743 1742 +f 1742 1743 1739 +f 62 63 1745 +f 1745 63 1744 +f 1745 36 37 +f 1745 37 60 +f 1745 60 62 +f 1739 1746 1855 +f 1739 1855 1856 +f 492 1857 35 +f 492 1742 1857 +f 1857 1742 34 +f 34 1742 1856 +f 1856 1742 1739 +f 1741 33 1746 +f 1746 33 1855 +f 669 668 1662 +f 1662 668 1784 +f 929 932 931 +f 929 931 928 +f 1747 681 1749 +f 1749 675 679 +f 1749 681 675 +f 679 677 384 +f 676 384 677 +f 1748 1747 1749 +f 385 1749 679 +f 385 679 384 +f 383 384 678 +f 1749 1851 1748 +f 288 1750 1751 +f 623 1750 622 +f 287 622 1750 +f 1750 288 287 +f 1248 288 1751 +f 1609 45 46 +f 45 1619 57 +f 1609 1619 45 +f 1406 1067 1017 +f 1439 1692 1325 +f 1756 1753 1754 +f 1754 1753 746 +f 1755 1756 1754 +f 1753 1756 1752 +f 1753 1752 673 +f 760 1752 1756 +f 816 806 1765 +f 805 833 1765 +f 698 438 442 +f 1757 698 442 +f 1757 700 699 +f 1531 1757 699 +f 489 1666 1665 +f 1758 1409 1651 +f 1758 1651 1650 +f 1804 1017 1759 +f 1801 1018 1848 +f 1848 1018 1760 +f 1760 1334 1844 +f 1334 1760 1018 +f 1018 1046 1334 +f 272 952 819 +f 1764 1766 817 +f 1764 817 1763 +f 1764 1763 1762 +f 1763 817 818 +f 1764 1765 833 +f 1762 1763 953 +f 1765 1762 816 +f 1766 1794 1767 +f 1767 845 1834 +f 1762 1765 1764 +f 1764 1794 1766 +f 1794 1764 833 +f 223 222 221 +f 46 1768 51 +f 1406 1017 1676 +f 1448 1438 1437 +f 1780 1546 1781 +f 1772 1887 2074 +f 2074 1886 1771 +f 583 1883 495 +f 1992 2164 472 +f 2164 1773 2163 +f 461 1772 2074 +f 1773 1772 2163 +f 1992 1773 2164 +f 2164 1774 473 +f 473 2226 2227 +f 472 2164 473 +f 1612 1775 1907 +f 1775 47 1915 +f 1800 47 1775 +f 1776 1800 1777 +f 1776 1777 1611 +f 1777 1800 1604 +f 1779 1047 1639 +f 1782 1780 1781 +f 1782 1781 1896 +f 1544 671 1545 +f 233 1783 222 +f 1321 1790 1726 +f 1726 1790 1725 +f 1646 1316 1417 +f 1417 1316 1805 +f 1805 1316 1415 +f 1784 1660 1662 +f 1786 1809 1663 +f 1784 762 1786 +f 1786 2022 1787 +f 1787 2022 1785 +f 1787 1784 1786 +f 762 1809 1786 +f 1663 2022 1786 +f 1581 1579 1643 +f 1679 1581 1643 +f 1755 760 1756 +f 749 744 1825 +f 1825 1796 749 +f 1828 694 696 +f 489 1668 1667 +f 489 503 1668 +f 1434 1791 1778 +f 1432 1792 1434 +f 1432 1434 1433 +f 1456 1457 1792 +f 1434 1792 1791 +f 1457 1791 1792 +f 834 835 1795 +f 1795 831 1794 +f 1794 831 1767 +f 832 1767 831 +f 845 1767 832 +f 833 1795 1794 +f 833 805 1795 +f 749 1796 1813 +f 1797 1798 1799 +f 1798 320 325 +f 601 1799 603 +f 1799 1798 603 +f 314 1799 601 +f 1799 314 1797 +f 294 1798 1797 +f 476 470 475 +f 475 470 1868 +f 1604 1800 1775 +f 1775 1612 1604 +f 1802 1804 1759 +f 1802 1759 1704 +f 1848 1803 1801 +f 1704 1759 1803 +f 1801 1803 1759 +f 1802 1676 1804 +f 431 448 443 +f 304 1797 314 +f 294 320 1798 +f 1805 1424 1806 +f 1829 1806 1425 +f 1830 1418 1808 +f 1418 1830 1413 +f 1807 1805 1806 +f 1649 1808 1418 +f 1830 1808 1807 +f 1809 762 1793 +f 1809 1793 1622 +f 526 527 878 +f 527 526 1789 +f 527 1789 1812 +f 527 1812 1810 +f 1810 747 527 +f 1755 1812 1811 +f 1812 1789 1811 +f 750 1811 1789 +f 1821 1911 1850 +f 1820 1823 1819 +f 1819 1823 1817 +f 525 1817 1818 +f 1823 1820 1815 +f 1815 1820 1816 +f 1824 1823 1912 +f 1824 1912 1827 +f 1911 1821 1822 +f 1912 1823 1881 +f 1881 1823 1815 +f 1814 1850 1911 +f 738 742 1850 +f 1821 741 1796 +f 1796 741 1813 +f 1821 1796 1822 +f 1825 1826 1822 +f 1822 1796 1825 +f 1544 1542 1543 +f 1780 1542 1544 +f 1545 1780 1544 +f 1659 1660 1661 +f 696 737 1828 +f 1828 737 1852 +f 1424 1831 1806 +f 1830 1807 1829 +f 1829 1414 1830 +f 1413 1830 1414 +f 1806 1831 1425 +f 1543 672 1842 +f 267 1839 1867 +f 1867 1839 1832 +f 848 1837 264 +f 264 1837 1835 +f 263 1835 1834 +f 1835 1836 1834 +f 1867 1832 1837 +f 1835 1837 1838 +f 1835 1838 1836 +f 1766 1836 1838 +f 1837 1832 1838 +f 1838 1832 1766 +f 819 1839 267 +f 1841 265 266 +f 265 1841 271 +f 1841 266 1840 +f 1840 1867 1833 +f 804 1843 1847 +f 1847 1843 803 +f 1849 1845 1871 +f 1870 1845 1849 +f 1844 1333 1870 +f 1844 1870 1849 +f 1842 746 1753 +f 1766 1767 1836 +f 844 837 1846 +f 1927 1847 802 +f 1847 1927 1846 +f 804 1846 830 +f 802 1847 803 +f 804 1847 1846 +f 750 1789 744 +f 744 1789 745 +f 749 750 744 +f 1669 1671 1666 +f 1760 1849 1848 +f 1871 1803 1848 +f 1871 1848 1849 +f 1844 1849 1760 +f 742 1821 1850 +f 1748 1851 1852 +f 358 1828 1914 +f 1828 1852 1914 +f 1914 1852 1851 +f 1914 1851 366 +f 1854 497 494 +f 459 497 490 +f 1854 460 1668 +f 497 1854 490 +f 490 1854 1668 +f 1856 1855 1368 +f 1959 35 1928 +f 1857 34 1928 +f 1857 1928 35 +f 1690 1858 1696 +f 273 1701 262 +f 1642 272 262 +f 837 1859 836 +f 1810 1812 1755 +f 738 1850 1877 +f 1866 428 1862 +f 369 1860 1861 +f 2039 1863 368 +f 1860 370 1862 +f 1860 1862 1864 +f 430 1863 1864 +f 430 1864 1862 +f 446 1862 428 +f 430 1862 446 +f 434 2018 1863 +f 39 1866 370 +f 39 48 1866 +f 1861 2039 1865 +f 1865 2039 374 +f 2039 368 374 +f 370 1866 1862 +f 48 428 1866 +f 470 469 1885 +f 470 1885 1868 +f 1770 1868 1769 +f 471 1770 1886 +f 471 1886 1869 +f 1868 1770 475 +f 1886 1887 1869 +f 1773 1991 1869 +f 1886 1770 1771 +f 1769 1868 1885 +f 1870 1333 1332 +f 1871 1909 1405 +f 1909 1871 1872 +f 1871 1845 1872 +f 1872 1845 1870 +f 1870 1332 1872 +f 776 1630 1761 +f 1761 1630 1873 +f 1788 1873 1630 +f 1876 1679 1874 +f 1680 1874 1679 +f 1876 1874 1933 +f 1874 1875 1930 +f 1631 1930 1875 +f 767 1631 780 +f 1876 1581 1679 +f 1846 1927 844 +f 1722 1709 1703 +f 538 672 528 +f 1867 1837 1833 +f 1833 1837 848 +f 848 1840 1833 +f 1814 1912 1881 +f 1879 676 677 +f 677 679 675 +f 676 1879 678 +f 1877 1879 677 +f 1877 1881 1879 +f 1879 1880 678 +f 678 1880 1878 +f 1877 1814 1881 +f 1879 1881 1815 +f 469 1882 1885 +f 1885 1882 1884 +f 1884 495 1885 +f 1885 495 1883 +f 1883 583 1771 +f 1887 1886 2074 +f 1883 1771 1769 +f 1770 1769 1771 +f 1887 1772 1773 +f 1991 1773 1992 +f 1885 1883 1769 +f 1405 1704 1803 +f 1405 1803 1871 +f 2496 1661 1787 +f 1787 1661 1660 +f 746 539 1905 +f 1889 1888 540 +f 1904 1987 542 +f 538 1888 1889 +f 1905 1889 537 +f 445 432 447 +f 1807 1806 1829 +f 1895 1893 1542 +f 1895 1542 1782 +f 1900 1893 1895 +f 1900 1895 1901 +f 1897 1891 1892 +f 2175 2246 892 +f 1902 529 1893 +f 1895 1782 1894 +f 1782 1896 1894 +f 1901 1895 1894 +f 529 1902 528 +f 2053 1890 1897 +f 1890 1891 1897 +f 1897 2246 2174 +f 2246 1897 1892 +f 543 541 1898 +f 1902 1900 1899 +f 1901 2043 1890 +f 1890 2053 1901 +f 534 1902 1899 +f 1902 1893 1900 +f 747 746 1903 +f 2041 878 1903 +f 1903 1904 542 +f 1903 542 2041 +f 746 1905 1903 +f 1903 1905 1904 +f 537 1904 1905 +f 1711 1720 1712 +f 1711 1712 1843 +f 1720 1711 1710 +f 1908 1907 1915 +f 1908 1606 1907 +f 528 1902 534 +f 1332 1331 1872 +f 1872 1331 1910 +f 1872 1910 1909 +f 1910 1335 1909 +f 1909 1335 1926 +f 1405 1909 1926 +f 802 822 1927 +f 844 1927 827 +f 1822 1827 1911 +f 1912 1814 1911 +f 1911 1827 1912 +f 1921 1712 1720 +f 1712 1921 1952 +f 1989 1913 1399 +f 675 1877 677 +f 1853 358 1914 +f 1935 1916 1936 +f 1916 1935 1915 +f 1429 1691 1467 +f 1429 1467 1428 +f 1917 1918 1919 +f 1919 1918 1920 +f 1919 1920 1917 +f 1917 1920 1918 +f 1850 1814 1877 +f 1369 1402 1922 +f 1922 1924 1906 +f 1906 1924 1923 +f 1922 1403 1924 +f 1924 1403 1729 +f 1924 1925 1923 +f 1924 1729 1925 +f 1923 1925 1329 +f 1330 1329 1926 +f 1329 1731 1926 +f 1680 1788 1874 +f 1788 1875 1874 +f 1580 1809 1622 +f 842 844 827 +f 827 1927 822 +f 1929 1933 856 +f 1932 1933 1930 +f 1930 2109 1932 +f 1876 1933 1929 +f 856 1933 1932 +f 1929 856 1580 +f 1934 745 526 +f 526 745 1789 +f 1935 1908 1915 +f 40 55 1935 +f 1935 1936 40 +f 1731 1937 1723 +f 1723 1926 1731 +f 1630 776 1631 +f 775 1631 776 +f 1822 1826 1827 +f 1938 494 1941 +f 1938 1941 1939 +f 1940 1381 1939 +f 1397 1939 1941 +f 1396 1941 494 +f 1854 494 1942 +f 1670 1945 1944 +f 1670 1944 2076 +f 1854 1942 500 +f 500 1942 1945 +f 1670 500 1945 +f 1946 1942 2057 +f 1943 1384 1946 +f 1382 1944 1946 +f 1943 1946 2057 +f 494 1938 1942 +f 1945 1946 1944 +f 1945 1942 1946 +f 1326 1985 1687 +f 1687 1985 1948 +f 1687 1948 1688 +f 1331 1949 1910 +f 1910 1949 1997 +f 1335 1910 1982 +f 439 435 437 +f 439 1950 407 +f 439 407 435 +f 1951 1853 366 +f 1950 1951 407 +f 825 802 823 +f 802 825 822 +f 657 1952 2002 +f 2002 1952 1921 +f 1953 2002 1979 +f 655 1956 2003 +f 1954 2307 2006 +f 2006 2340 2004 +f 2221 1958 1957 +f 1957 2005 2264 +f 1959 1906 1328 +f 1947 1328 1963 +f 1398 1337 1397 +f 1960 2158 2171 +f 1960 2171 1961 +f 1962 1961 2169 +f 2169 1940 1962 +f 1940 1397 1339 +f 1398 1990 1963 +f 1328 1947 1959 +f 1340 1963 1328 +f 1923 1328 1906 +f 269 268 265 +f 265 268 266 +f 1988 1245 310 +f 1988 310 285 +f 617 285 311 +f 311 285 310 +f 317 328 1869 +f 1966 2040 602 +f 317 316 1964 +f 317 1869 1991 +f 1965 2040 1966 +f 1997 1949 1948 +f 1969 1974 1968 +f 1974 1970 1984 +f 1970 1974 1969 +f 1326 1971 1984 +f 1984 1971 1974 +f 1974 1971 1967 +f 1967 1971 1972 +f 43 1973 2072 +f 1974 1967 1968 +f 1341 1968 1973 +f 1338 1968 1341 +f 1973 1968 1967 +f 1968 1338 1969 +f 1967 1972 2072 +f 1967 2072 1973 +f 1824 1975 1817 +f 1818 1817 1975 +f 1827 1975 1824 +f 1824 1817 1823 +f 1827 1826 877 +f 1827 877 1975 +f 877 1818 1975 +f 1818 877 555 +f 1716 1717 1976 +f 625 1976 1717 +f 1976 1980 1716 +f 1716 1980 1977 +f 1977 2002 1978 +f 1977 1980 1981 +f 2086 1980 1976 +f 2086 1981 1980 +f 1981 1956 1977 +f 1979 1956 655 +f 1956 1979 1977 +f 1977 1979 2002 +f 498 1396 497 +f 1910 1997 1982 +f 1982 1983 2020 +f 1969 2021 1983 +f 1969 1983 1970 +f 1970 1983 1986 +f 1970 1986 1984 +f 1984 1985 1326 +f 1948 1985 1997 +f 1997 1986 1982 +f 1982 1986 1983 +f 269 265 271 +f 269 271 270 +f 540 537 1889 +f 1987 1904 537 +f 1826 745 1934 +f 1826 1934 877 +f 1951 1950 1853 +f 1989 1990 1396 +f 1990 1989 1963 +f 1963 1989 1947 +f 316 1991 1992 +f 2170 1995 1994 +f 345 1994 1995 +f 1993 2170 1994 +f 473 2170 1993 +f 1994 1996 1993 +f 2019 316 472 +f 498 1989 1396 +f 1985 1986 1997 +f 1986 1985 1984 +f 494 497 1396 +f 370 1860 369 +f 2039 1864 1863 +f 1841 1840 848 +f 435 1998 437 +f 533 2013 1999 +f 533 2042 2013 +f 1999 2013 532 +f 437 1998 2016 +f 2496 2037 530 +f 530 2037 1999 +f 533 1999 531 +f 2037 531 1999 +f 1899 1900 2001 +f 1899 2001 1898 +f 1898 2000 543 +f 2000 1898 2001 +f 657 2002 1953 +f 2014 615 2101 +f 615 2014 1244 +f 1398 1340 1337 +f 54 53 55 +f 1860 1864 1861 +f 2001 2052 2000 +f 2000 2052 2119 +f 547 2000 2119 +f 547 2119 545 +f 1953 1979 656 +f 656 1979 655 +f 655 2003 2153 +f 654 2153 653 +f 2153 654 655 +f 2153 2003 2006 +f 2004 2154 2153 +f 2004 2264 2005 +f 2004 2153 2006 +f 619 611 2008 +f 611 618 2008 +f 2008 618 2007 +f 2012 2069 2122 +f 2011 42 2010 +f 2011 2010 2157 +f 2011 2157 2105 +f 2011 43 2009 +f 2009 44 42 +f 2069 2012 2070 +f 1468 2122 44 +f 42 44 2122 +f 1841 848 2073 +f 1901 1894 2043 +f 855 1932 1931 +f 853 1931 1932 +f 540 541 543 +f 1861 1864 2039 +f 1990 1941 1396 +f 2014 2086 625 +f 2014 625 1244 +f 1998 435 2015 +f 1998 2015 2016 +f 2015 435 2017 +f 427 2015 2017 +f 427 2017 367 +f 408 2015 427 +f 53 54 2058 +f 434 2016 2018 +f 2015 2018 2016 +f 316 2019 1965 +f 1965 2019 2040 +f 2040 2019 1993 +f 315 1996 1994 +f 315 1994 345 +f 1996 319 2040 +f 2040 1993 1996 +f 1337 2020 2021 +f 1339 2021 1338 +f 2020 1983 2021 +f 1969 1338 2021 +f 2022 1663 857 +f 857 856 855 +f 2026 2030 2120 +f 2120 2030 2023 +f 2024 2030 2029 +f 2029 2027 895 +f 2025 2028 2027 +f 2027 2026 2025 +f 2027 2030 2026 +f 2028 895 2027 +f 2027 2029 2030 +f 2030 2024 2023 +f 2023 2031 2501 +f 1892 2033 2034 +f 2034 892 2246 +f 2023 2024 2031 +f 2031 2024 2035 +f 2501 2035 2033 +f 892 2034 2035 +f 2033 2035 2034 +f 2024 2029 2035 +f 2031 2035 2501 +f 2066 2022 2067 +f 2067 2022 2036 +f 857 2036 2022 +f 1785 2022 2066 +f 2051 2066 2026 +f 2025 2026 2067 +f 2067 2026 2066 +f 2026 2120 2051 +f 531 2037 2051 +f 1880 1815 1816 +f 2056 1880 1816 +f 524 357 2125 +f 357 2038 2125 +f 2038 2056 2125 +f 1815 1880 1879 +f 368 1863 2018 +f 2012 1423 2070 +f 857 1663 856 +f 546 2041 542 +f 380 381 378 +f 2043 2013 2042 +f 2045 2046 2054 +f 2113 2111 2200 +f 2112 2048 2049 +f 2113 789 2046 +f 2113 799 789 +f 2111 829 2048 +f 2047 828 829 +f 2115 2049 2048 +f 771 1841 2073 +f 2052 2053 2119 +f 2173 2053 2127 +f 2174 2127 1897 +f 1897 2127 2053 +f 2053 2052 1901 +f 1901 2052 1900 +f 2001 1900 2052 +f 2044 2110 2045 +f 1880 2056 1878 +f 1878 2056 2038 +f 495 584 496 +f 2057 1938 1939 +f 1381 2057 1939 +f 53 2058 1423 +f 791 2059 2064 +f 2060 2064 2061 +f 2061 798 2063 +f 797 2060 2063 +f 2061 2063 2060 +f 798 2062 2063 +f 2063 2062 794 +f 2143 2065 876 +f 1942 1938 2057 +f 1943 2057 1381 +f 1887 1773 1869 +f 1785 2066 2051 +f 772 771 770 +f 772 770 779 +f 2018 408 368 +f 408 2018 2015 +f 2008 2007 617 +f 617 2007 2068 +f 1861 373 2050 +f 2071 2070 2058 +f 2073 791 787 +f 2111 2047 829 +f 461 462 1772 +f 1771 583 2074 +f 2074 583 2145 +f 461 2074 2145 +f 1774 2226 473 +f 1774 2164 463 +f 463 2164 2163 +f 1878 2038 379 +f 496 2076 2077 +f 560 2150 1382 +f 496 2077 2083 +f 496 2083 2078 +f 2078 2079 582 +f 2281 2079 2081 +f 2081 2080 2376 +f 2376 2080 586 +f 2080 2081 2085 +f 2085 2084 2233 +f 2150 2082 2077 +f 2079 2078 2083 +f 2083 2077 2079 +f 2077 2084 2079 +f 2084 2077 2082 +f 2084 2085 2081 +f 2081 2079 2084 +f 2076 1944 2077 +f 1944 1382 2150 +f 531 2051 2120 +f 2086 2014 2100 +f 2099 2100 2102 +f 2086 2099 1981 +f 1981 2099 2088 +f 2088 2099 2102 +f 2088 2102 1955 +f 1955 2102 2089 +f 2089 2102 2090 +f 2311 2096 2207 +f 2311 2207 2091 +f 348 2282 347 +f 2091 2092 2093 +f 2093 2421 2351 +f 2351 2421 2094 +f 2421 909 2094 +f 2092 630 2093 +f 2091 2205 2092 +f 2092 2205 348 +f 348 596 2092 +f 2089 2090 2345 +f 626 2098 2096 +f 2096 2097 626 +f 626 2345 2098 +f 2100 2099 2086 +f 2100 2014 2101 +f 2155 2087 615 +f 2155 615 616 +f 2161 2152 2168 +f 2068 2167 613 +f 613 2167 2168 +f 2223 2098 2090 +f 2090 2098 2345 +f 2101 615 2087 +f 2161 616 2152 +f 2108 2107 1962 +f 2108 2106 2177 +f 2105 2106 2108 +f 2107 2108 2177 +f 2009 43 2104 +f 43 2072 2104 +f 2110 2047 2111 +f 2110 2111 2113 +f 2111 2112 2200 +f 2112 2111 2048 +f 771 2114 769 +f 2110 2044 2047 +f 2045 2110 2046 +f 2046 2110 2113 +f 655 654 656 +f 2049 2115 2116 +f 2118 801 656 +f 2118 656 2117 +f 800 2116 2118 +f 800 2118 2117 +f 653 2117 654 +f 653 652 2117 +f 2117 652 800 +f 799 652 651 +f 2117 656 654 +f 545 2119 536 +f 536 2119 2178 +f 536 2178 2147 +f 2120 2023 2498 +f 2120 2498 531 +f 2088 2003 1956 +f 2121 379 405 +f 391 42 2122 +f 391 2122 2123 +f 2123 2122 2069 +f 2010 42 391 +f 1940 1339 1338 +f 1940 1338 1962 +f 357 405 379 +f 797 2063 2166 +f 792 797 2166 +f 2063 795 2166 +f 472 1993 2019 +f 2124 1931 858 +f 859 855 2124 +f 2125 1816 1820 +f 523 2125 1820 +f 1816 2125 2056 +f 1820 2126 523 +f 2068 613 617 +f 2178 2053 2173 +f 2119 2053 2178 +f 2036 863 638 +f 863 2036 857 +f 895 2028 638 +f 2029 2128 2035 +f 2128 2029 890 +f 2275 2035 2128 +f 2025 2067 2028 +f 2028 2036 638 +f 2036 2028 2067 +f 2294 2130 2295 +f 2298 2130 2138 +f 2131 2134 2135 +f 2131 897 2134 +f 2362 2133 2363 +f 897 2363 2133 +f 2134 897 2133 +f 2135 2134 2360 +f 2135 2137 2330 +f 2330 2137 2136 +f 2129 2130 2294 +f 2360 2138 2358 +f 2360 2358 2137 +f 2360 2134 2133 +f 2360 2137 2135 +f 2140 2251 2141 +f 2141 2251 2195 +f 2195 874 2350 +f 2251 874 2195 +f 873 2350 874 +f 2143 876 2141 +f 2165 2143 2141 +f 2141 876 2140 +f 875 2140 876 +f 2304 552 2293 +f 2142 2143 2165 +f 550 2143 2142 +f 551 2142 555 +f 1818 555 525 +f 2126 525 553 +f 1820 1819 2126 +f 1817 525 1819 +f 1819 525 2126 +f 1940 2169 1379 +f 546 536 544 +f 770 793 851 +f 770 851 785 +f 583 2078 2145 +f 461 585 2259 +f 461 2259 2260 +f 852 783 784 +f 375 580 367 +f 367 580 386 +f 386 387 400 +f 427 400 2146 +f 374 2144 409 +f 2201 409 2144 +f 773 785 784 +f 536 2147 2148 +f 544 536 2148 +f 544 2148 882 +f 1944 2150 2077 +f 2069 2070 2071 +f 2123 420 391 +f 2151 2123 2071 +f 2123 2069 2071 +f 420 2123 2151 +f 796 2062 2054 +f 796 2054 2046 +f 2062 796 794 +f 2100 2101 2087 +f 2152 613 2168 +f 2153 2154 653 +f 651 1958 650 +f 2196 651 652 +f 2196 652 653 +f 2196 653 2154 +f 2155 616 2161 +f 2096 2223 2156 +f 1962 2107 1961 +f 2245 2176 2157 +f 2176 2158 1960 +f 1960 1961 2107 +f 2148 2159 882 +f 882 875 884 +f 2257 887 2160 +f 2223 2155 2156 +f 2155 2161 2156 +f 2150 560 2162 +f 2082 2162 2233 +f 2203 2162 560 +f 2150 2162 2082 +f 2082 2233 2084 +f 1772 462 2163 +f 2071 422 417 +f 552 2165 2293 +f 2165 552 2142 +f 2003 2088 1955 +f 2003 1955 1954 +f 2087 2155 2223 +f 2167 608 2168 +f 608 2161 2168 +f 585 2078 582 +f 2079 2281 582 +f 2145 2078 585 +f 2259 585 582 +f 1993 472 473 +f 2200 2112 800 +f 2200 800 652 +f 786 787 2283 +f 2227 1995 2170 +f 2169 1961 1379 +f 1379 1961 2171 +f 2274 2171 2158 +f 1380 2172 1378 +f 1379 2171 1380 +f 1380 2171 2274 +f 2173 2127 2180 +f 2180 2127 2174 +f 2180 2174 2175 +f 2175 2174 2246 +f 2090 2100 2087 +f 2176 1960 2177 +f 1960 2107 2177 +f 2176 2177 2106 +f 2176 2106 2157 +f 2178 2173 2180 +f 2178 2180 889 +f 889 2257 2179 +f 889 2180 891 +f 2178 889 2179 +f 2147 2178 2179 +f 2182 2229 2181 +f 2183 2229 2182 +f 667 641 2183 +f 2183 641 2229 +f 2183 2182 643 +f 643 2182 870 +f 2183 643 642 +f 2139 2141 2195 +f 2139 2195 2129 +f 2359 2184 2136 +f 2187 2381 2392 +f 2186 2188 2187 +f 2186 2187 2437 +f 2187 2392 2437 +f 2188 2186 2191 +f 2451 2192 2435 +f 2457 2193 2194 +f 2193 2457 2190 +f 2193 2451 2435 +f 2380 2354 2136 +f 2136 2354 2330 +f 2184 2185 2136 +f 2184 2350 2185 +f 2350 873 2381 +f 2359 2129 2184 +f 2195 2184 2129 +f 2195 2350 2184 +f 2196 2154 2004 +f 2196 2004 2005 +f 1958 2005 1957 +f 2196 2005 1958 +f 2196 1958 651 +f 2223 2090 2087 +f 396 398 400 +f 398 2146 400 +f 2198 2197 2250 +f 2199 860 2250 +f 2200 652 799 +f 2003 1954 2006 +f 2090 2102 2100 +f 2201 402 2202 +f 2273 410 2202 +f 2146 398 2201 +f 2201 2202 409 +f 2200 799 2113 +f 566 565 567 +f 567 565 356 +f 356 565 2149 +f 1378 1379 1380 +f 1378 2266 1383 +f 523 549 2204 +f 2204 2292 520 +f 2292 2204 549 +f 523 2204 2125 +f 2224 2205 2206 +f 610 2206 2205 +f 610 2205 2091 +f 2161 2207 2156 +f 2161 608 2207 +f 2156 2207 2096 +f 610 2091 2207 +f 610 2207 608 +f 2206 346 2224 +f 346 2206 2208 +f 2219 795 788 +f 795 2219 2263 +f 796 789 788 +f 2210 553 552 +f 2210 2314 554 +f 2304 2210 552 +f 521 2204 520 +f 524 2204 521 +f 2278 521 520 +f 521 2278 522 +f 356 522 567 +f 2228 2261 2211 +f 2349 2262 2213 +f 2213 2262 2258 +f 2212 2258 2281 +f 2258 2212 2213 +f 2349 2213 2214 +f 2214 589 2349 +f 2213 2212 2215 +f 2379 2214 2215 +f 2212 2376 2215 +f 2411 2377 556 +f 556 2377 2397 +f 2215 2216 2379 +f 2379 2216 2377 +f 2228 2211 2217 +f 593 2211 2357 +f 2357 2211 2218 +f 2217 2211 590 +f 2211 2261 2218 +f 2243 2010 419 +f 2010 391 419 +f 2220 2221 2222 +f 2355 2398 2222 +f 2398 2424 2222 +f 2222 2424 2287 +f 2224 346 1995 +f 2224 2227 2282 +f 2227 2226 2225 +f 2225 2226 590 +f 2224 1995 2227 +f 2282 2227 2225 +f 463 2228 1774 +f 639 854 643 +f 883 2257 2160 +f 2147 2179 883 +f 883 2179 2257 +f 2231 2203 2235 +f 1376 2353 2234 +f 2230 573 2235 +f 2233 2162 2231 +f 2231 2235 2232 +f 2234 2353 2230 +f 1386 2234 2230 +f 1386 2230 2235 +f 2209 1386 2235 +f 2209 2235 2203 +f 2203 2266 1377 +f 424 393 2236 +f 2236 393 411 +f 411 393 412 +f 2320 412 393 +f 2237 2157 2010 +f 2010 2243 2237 +f 2157 2237 2245 +f 2176 2245 2158 +f 2158 2245 2244 +f 2158 2244 2239 +f 2241 2239 2242 +f 2242 2239 2244 +f 2244 413 2242 +f 2158 2239 2240 +f 1370 2238 2240 +f 2239 1370 2240 +f 1370 2241 2272 +f 425 2242 413 +f 2244 2237 2243 +f 2034 2246 1892 +f 643 864 639 +f 639 864 860 +f 860 2248 2250 +f 2248 860 2247 +f 2252 581 2255 +f 2254 1371 2256 +f 2256 1371 2255 +f 2253 2254 2256 +f 561 1372 2254 +f 561 2254 2253 +f 2248 2312 2198 +f 554 2292 549 +f 2258 2259 582 +f 2258 582 2281 +f 2259 2262 2260 +f 2261 2260 2218 +f 2218 2262 2349 +f 2258 2262 2259 +f 2260 2262 2218 +f 861 2197 2198 +f 2006 2307 2340 +f 2264 2265 1957 +f 2398 2393 647 +f 2264 2004 2340 +f 2264 2340 2265 +f 2340 2371 2265 +f 2256 2255 581 +f 1378 2172 2266 +f 2266 2172 1377 +f 1386 2271 2267 +f 2268 2269 2267 +f 2269 1387 2267 +f 1387 2270 2267 +f 2271 1377 2268 +f 2268 2267 2271 +f 2172 2268 1377 +f 2238 1370 2404 +f 2404 2268 2238 +f 2238 2268 2172 +f 2271 1386 2209 +f 2274 2158 2240 +f 2240 2238 2274 +f 2274 2238 2172 +f 410 2273 393 +f 2172 1380 2274 +f 2128 890 2275 +f 637 861 636 +f 885 886 2276 +f 2388 2391 888 +f 886 2391 2276 +f 888 2391 886 +f 2278 2277 567 +f 567 2277 566 +f 2316 2400 2279 +f 2279 520 2316 +f 2315 2316 520 +f 2277 568 566 +f 2278 567 522 +f 2277 2278 2279 +f 2277 2279 568 +f 568 2279 2400 +f 2253 2256 2280 +f 2280 2256 581 +f 2281 2081 2212 +f 2081 2376 2212 +f 2231 2232 2233 +f 595 347 2225 +f 1774 2228 2217 +f 1774 590 2226 +f 590 1774 2217 +f 2284 2283 2285 +f 2222 2287 2286 +f 2222 2286 2220 +f 2283 2249 2286 +f 2286 2285 2283 +f 2286 2370 2285 +f 2289 2290 2369 +f 2289 2369 2288 +f 2369 2448 2438 +f 2438 2448 2409 +f 635 2291 2290 +f 2369 2290 2448 +f 2291 2448 2290 +f 2289 646 2290 +f 2287 2288 2370 +f 2287 2370 2286 +f 2284 2285 665 +f 2293 2165 2139 +f 2129 2294 2293 +f 2293 2139 2129 +f 2296 2302 2387 +f 2295 2296 2294 +f 2295 2130 2300 +f 2300 2298 2299 +f 2297 2361 2399 +f 2300 2130 2298 +f 2298 2297 2299 +f 2302 2295 2300 +f 353 2301 2420 +f 2420 352 353 +f 351 578 352 +f 2295 2302 2296 +f 2387 2302 353 +f 2302 2301 353 +f 2347 2307 1954 +f 2342 2347 1954 +f 2224 2282 2205 +f 348 2205 2282 +f 2175 892 2303 +f 2275 881 892 +f 892 881 2303 +f 2293 2294 354 +f 2304 2293 354 +f 2314 2306 2394 +f 2305 2394 2419 +f 2210 2306 2314 +f 2306 2304 354 +f 664 2408 2308 +f 640 2308 659 +f 2181 659 2309 +f 2341 2347 2310 +f 2098 2223 2096 +f 2097 2096 2311 +f 2225 347 2282 +f 636 861 2198 +f 2198 2312 636 +f 2247 2313 2502 +f 2247 864 2313 +f 2247 860 864 +f 2347 2341 2307 +f 2316 2315 2305 +f 2394 2305 2315 +f 2305 355 2316 +f 2400 2316 355 +f 2315 2314 2394 +f 1392 1373 1391 +f 2275 890 894 +f 2341 2340 2307 +f 574 2085 575 +f 1392 1387 1374 +f 393 2273 2319 +f 2318 2317 2322 +f 397 388 2317 +f 2317 2318 395 +f 2322 2317 388 +f 2319 401 2318 +f 393 2319 2320 +f 2319 390 2444 +f 2318 390 2319 +f 2320 2319 2444 +f 390 2321 2442 +f 2440 2321 389 +f 2440 2442 2321 +f 2443 390 2442 +f 2322 389 2321 +f 389 2322 388 +f 390 2318 2321 +f 2321 2318 2322 +f 2244 2245 2237 +f 2338 2354 2188 +f 2459 2458 2336 +f 2480 2334 2335 +f 2333 2334 2477 +f 2477 2478 2325 +f 2333 2336 2332 +f 2477 2325 2460 +f 2477 2460 2333 +f 2337 2326 2331 +f 2330 2131 2135 +f 2329 2331 2326 +f 2328 2329 2131 +f 2131 2330 2328 +f 2330 2339 2328 +f 2339 2331 2328 +f 2336 2333 2460 +f 2325 2337 2460 +f 2325 921 2337 +f 2191 2189 2459 +f 2459 2189 2458 +f 2332 2458 2189 +f 2336 2458 2332 +f 2334 896 2456 +f 2335 2456 2455 +f 896 2334 2333 +f 896 2333 2332 +f 2334 2456 2335 +f 2324 2188 2459 +f 2459 2188 2191 +f 2337 2336 2460 +f 2336 2331 2324 +f 2331 2336 2337 +f 2324 2338 2188 +f 2339 2330 2338 +f 2339 2338 2324 +f 2331 2339 2324 +f 906 633 2341 +f 2341 633 2371 +f 2341 2371 2340 +f 520 2279 2278 +f 2342 2089 2345 +f 575 2085 2232 +f 2211 593 590 +f 2347 2342 2346 +f 2343 2344 2346 +f 2343 2346 2342 +f 626 2343 2345 +f 2345 2343 2342 +f 2436 2346 2344 +f 905 2436 627 +f 627 2436 2344 +f 2348 906 2310 +f 2347 2346 2310 +f 2311 2351 2097 +f 2230 572 573 +f 2349 2357 2218 +f 414 2407 2323 +f 2091 2093 2351 +f 2351 2311 2091 +f 2351 2094 2097 +f 2097 2094 626 +f 586 574 2352 +f 576 586 2352 +f 572 2230 2353 +f 569 2353 1376 +f 2380 2187 2354 +f 2393 2355 2265 +f 1957 2265 2355 +f 2221 2355 2222 +f 2355 2221 1957 +f 2298 2360 2297 +f 2133 2362 2360 +f 574 2356 2352 +f 2214 2213 2215 +f 1374 2403 1392 +f 1388 1374 1387 +f 2359 2136 2358 +f 2137 2358 2136 +f 2138 2359 2358 +f 2359 2130 2129 +f 2138 2130 2359 +f 2361 2362 2363 +f 2363 557 2361 +f 2362 2297 2360 +f 2297 2362 2361 +f 557 2363 2364 +f 2132 898 899 +f 2132 899 2363 +f 597 593 2366 +f 593 2357 2366 +f 2357 2349 2366 +f 395 394 2317 +f 394 397 2317 +f 2367 2368 2450 +f 891 2303 2414 +f 2414 2303 2415 +f 2368 881 2450 +f 881 2368 2415 +f 2450 893 2367 +f 881 2415 2303 +f 664 665 2410 +f 2369 2370 2288 +f 2370 2369 2438 +f 2185 2380 2136 +f 2371 633 2393 +f 2383 2372 2305 +f 2384 2387 353 +f 571 2375 570 +f 2375 2372 2374 +f 2373 353 352 +f 353 2373 2384 +f 2385 2372 2375 +f 2374 2372 2383 +f 2374 2383 2373 +f 2373 2383 2384 +f 570 2375 2374 +f 570 2374 2373 +f 2215 2376 2216 +f 2378 2377 2216 +f 2216 2376 2378 +f 589 2214 2379 +f 589 2379 592 +f 2377 592 2379 +f 589 2366 2349 +f 889 891 2426 +f 2426 891 2414 +f 2425 2414 2417 +f 2417 2388 887 +f 2416 2426 2425 +f 2381 2187 2380 +f 2350 2381 2185 +f 2381 2380 2185 +f 2393 2265 2371 +f 2305 2419 2383 +f 2384 2382 2387 +f 2385 355 2372 +f 2306 354 2382 +f 2382 354 2386 +f 2382 2386 2387 +f 354 2296 2386 +f 2366 589 594 +f 592 594 589 +f 665 2285 2370 +f 2389 2390 2423 +f 2388 2417 2389 +f 888 887 2388 +f 2391 2388 2422 +f 2422 2388 2389 +f 2138 2360 2298 +f 2399 2361 350 +f 2420 2356 351 +f 351 352 2420 +f 2395 558 2352 +f 2352 558 576 +f 558 2395 350 +f 2395 2352 2356 +f 2396 2400 2385 +f 576 2378 2376 +f 576 2397 2378 +f 2404 2269 2268 +f 2423 873 2422 +f 2398 2355 2393 +f 2424 2398 649 +f 2302 2300 2301 +f 2420 2301 2399 +f 2420 2399 2395 +f 2297 2399 2299 +f 2399 2301 2300 +f 2299 2399 2300 +f 2400 2396 568 +f 2400 355 2385 +f 2378 2397 2377 +f 2095 629 631 +f 631 629 2401 +f 2402 2403 1375 +f 2269 2404 2405 +f 2406 1395 2462 +f 2406 2405 1395 +f 2404 1370 1395 +f 2404 1395 2405 +f 1370 2239 2241 +f 2412 659 2308 +f 2370 2438 2410 +f 664 2410 2408 +f 2408 2410 2438 +f 2346 2348 2310 +f 2436 2348 2346 +f 556 2397 559 +f 2439 556 559 +f 2408 2412 2308 +f 2132 2363 897 +f 2394 2306 2382 +f 2386 2296 2387 +f 2419 2384 2383 +f 2384 2419 2382 +f 2412 2408 2413 +f 2408 2409 2413 +f 2390 2415 2368 +f 2390 2368 2423 +f 2423 2367 2392 +f 2368 2367 2423 +f 2416 2417 887 +f 2425 2417 2416 +f 2437 2192 2186 +f 913 646 634 +f 634 2431 2418 +f 2382 2419 2394 +f 2093 629 2421 +f 2421 2095 909 +f 2422 2389 2423 +f 648 2288 2424 +f 2432 633 2348 +f 2484 2348 905 +f 2436 905 2348 +f 2372 355 2305 +f 2425 2426 2414 +f 2463 2430 2427 +f 2327 2428 2429 +f 2429 2428 2489 +f 2429 2329 2327 +f 2429 2427 2430 +f 2429 2430 2329 +f 2131 2329 2430 +f 2131 2430 2463 +f 2464 898 2132 +f 2464 2132 2463 +f 2418 2486 634 +f 2432 2348 2484 +f 2484 905 908 +f 2434 666 865 +f 2435 2194 2193 +f 2431 633 2432 +f 2344 909 627 +f 2192 893 2435 +f 2192 2437 893 +f 2409 2408 2438 +f 873 2423 2392 +f 2440 2461 2442 +f 2442 2461 2443 +f 2242 425 2441 +f 1375 2440 389 +f 392 2444 2443 +f 2457 2194 872 +f 2413 2409 2445 +f 2445 2409 2448 +f 920 2447 2448 +f 2448 2447 2445 +f 2445 2446 2413 +f 2328 2331 2329 +f 2329 2326 2327 +f 2390 2417 2414 +f 2390 2414 2415 +f 920 2448 635 +f 2449 594 349 +f 899 2364 2363 +f 2418 2431 2432 +f 865 666 2446 +f 2186 2192 2191 +f 2191 2192 2451 +f 2189 2451 2190 +f 2193 2190 2451 +f 2427 2429 2452 +f 2454 556 2439 +f 2457 2455 2456 +f 868 2457 872 +f 2337 2327 2326 +f 917 2428 2327 +f 917 2327 921 +f 2457 2456 2190 +f 2190 2456 896 +f 2389 2417 2390 +f 2337 921 2327 +f 2418 2432 2433 +f 901 632 349 +f 2448 2291 635 +f 2324 2459 2336 +f 2433 2432 2484 +f 2464 903 898 +f 2454 591 2411 +f 2461 2462 2443 +f 2443 2462 2441 +f 2272 2441 2462 +f 2474 2486 2418 +f 2433 2474 2418 +f 2463 2427 2464 +f 2466 2468 2467 +f 2470 2467 2468 +f 2470 2468 2473 +f 916 2468 2465 +f 2465 918 916 +f 2490 2485 916 +f 916 2485 2468 +f 2464 2427 2452 +f 2466 902 2476 +f 2466 2476 2465 +f 2465 2476 2452 +f 902 2466 2469 +f 2469 2466 2467 +f 2491 2475 2471 +f 2484 908 2471 +f 2471 908 904 +f 2475 2433 2471 +f 2473 2468 2485 +f 2470 2473 2491 +f 2467 2470 2494 +f 2475 2474 2433 +f 2474 2475 2473 +f 2473 2475 2491 +f 2486 2485 634 +f 2464 2476 903 +f 2452 2476 2464 +f 2364 2454 2439 +f 2477 2480 2478 +f 2480 2479 2478 +f 2364 899 2453 +f 2481 588 900 +f 899 2487 2453 +f 2487 900 2453 +f 2492 901 2482 +f 2481 2483 2482 +f 2482 2493 2492 +f 901 349 2482 +f 635 919 920 +f 2433 2484 2471 +f 913 634 2485 +f 2474 2473 2486 +f 2477 2334 2480 +f 2488 903 902 +f 2493 2483 2469 +f 2483 2481 2469 +f 2469 2481 902 +f 902 2481 2488 +f 2488 2487 903 +f 2482 588 2481 +f 2490 910 913 +f 2490 913 2485 +f 2473 2485 2486 +f 2491 2471 904 +f 2470 2491 2494 +f 2493 2469 2494 +f 2494 2469 2467 +f 2466 2465 2468 +f 2487 898 903 +f 2494 2491 904 +f 2494 904 2472 +f 2483 2493 2482 +f 2189 2190 896 +f 2488 900 2487 +f 1180 1181 1182 +f 1479 764 1294 +f 1295 1294 764 +f 262 272 273 +f 2495 232 715 +f 2497 531 2498 +f 2032 2033 2499 +f 1891 2499 1892 +f 1892 2499 2033 +f 2499 2501 2032 +f 2499 2497 2498 +f 2500 1891 2042 +f 2042 1891 1890 +f 2032 2501 2033 +f 2501 2499 2498 +f 2501 2498 2023 +f 2500 2499 1891 +f 2499 2500 2497 +f 2055 2065 550 +f 2247 2502 2248 +f 636 2312 872 +f 894 2503 872 +f 872 2503 636 +f 16 21 1227 +f 15 21 16 +f 1908 53 52 +f 11 130 131 +f 65 1228 79 +f 97 5 120 +f 97 151 5 +f 161 189 148 +f 170 149 138 +f 409 410 373 +f 493 519 492 +f 619 624 606 +f 739 742 738 +f 782 767 780 +f 907 628 2472 +f 1073 1072 1043 +f 1068 1070 1067 +f 1033 1078 1032 +f 1122 1119 1120 +f 485 1206 1133 +f 1153 338 337 +f 1172 1171 274 +f 1098 1079 1010 +f 969 968 1270 +f 1552 1281 1280 +f 1078 1075 1076 +f 43 2105 1973 +f 1016 1324 1554 +f 1293 669 761 +f 1517 710 1551 +f 1513 1512 1514 +f 1657 1662 1660 +f 1710 1718 1720 +f 1843 1712 803 +f 1733 1722 1732 +f 32 1739 1743 +f 294 1797 304 +f 1676 1017 1804 +f 273 267 268 +f 1930 767 2109 +f 2050 373 372 +f 1861 1865 373 +f 495 496 583 +f 596 630 2092 +f 368 2144 374 +f 42 2011 2009 +f 2141 2139 2165 +f 2191 2451 2189 +f 2235 573 2232 +f 388 2402 389 +f 2198 2250 2248 +f 659 2181 640 +f 2478 921 2325 +f 665 2370 2410 +# 5000 faces, 0 coords texture + +# End of File diff --git a/testsuite/render-bunny/bunny.xml b/testsuite/render-bunny/bunny.xml new file mode 100644 index 000000000..80c39d5d8 --- /dev/null +++ b/testsuite/render-bunny/bunny.xml @@ -0,0 +1,21 @@ + + + + color Cs 0.35 0.35 0.35; shader matte layer1; + + + color Cs 0.75 0.25 0.25; shader matte layer1; + + + color Cs 0.25 0.25 0.75; shader matte layer1; + + + color Cs 0.25 0.25 0.25; shader matte layer1; + + + + + + float power 100; shader emitter layer1 + + diff --git a/testsuite/render-bunny/emitter.osl b/testsuite/render-bunny/emitter.osl new file mode 100644 index 000000000..b026b63e0 --- /dev/null +++ b/testsuite/render-bunny/emitter.osl @@ -0,0 +1,23 @@ +// Copyright Contributors to the Open Shading Language project. +// SPDX-License-Identifier: BSD-3-Clause +// https://github.com/AcademySoftwareFoundation/OpenShadingLanguage + + +surface +emitter + [[ string description = "Lambertian emitter material" ]] +( + float power = 1 + [[ string description = "Total power of the light", + float UImin = 0 ]], + color Cs = 1 + [[ string description = "Base color", + float UImin = 0, float UImax = 1 ]] + ) +{ + // Because emission() expects a weight in radiance, we must convert by dividing + // the power (in Watts) by the surface area and the factor of PI implied by + // uniform emission over the hemisphere. N.B.: The total power is BEFORE Cs + // filters the color! + Ci = (power / (M_PI * surfacearea())) * Cs * emission(); +} diff --git a/testsuite/render-bunny/envmap.osl b/testsuite/render-bunny/envmap.osl new file mode 100644 index 000000000..7e5906f05 --- /dev/null +++ b/testsuite/render-bunny/envmap.osl @@ -0,0 +1,15 @@ +// Copyright Contributors to the Open Shading Language project. +// SPDX-License-Identifier: BSD-3-Clause +// https://github.com/AcademySoftwareFoundation/OpenShadingLanguage + +shader envmap(float Kb = 1, string filename = "") +{ + vector dir = normalize(I); + float radial = atan2(-dir[2], dir[0]); + float nradial = acos(dir[1]); + float r = 0.5 * sin(nradial * 0.5); + float tu = 0.5 + r * cos(radial); + float tv = 0.5 - r * sin(radial); + color c = texture(filename, tu, tv); + Ci = Kb * c * background(); +} diff --git a/testsuite/render-bunny/matte.osl b/testsuite/render-bunny/matte.osl new file mode 100644 index 000000000..a8c6f187e --- /dev/null +++ b/testsuite/render-bunny/matte.osl @@ -0,0 +1,19 @@ +// Copyright Contributors to the Open Shading Language project. +// SPDX-License-Identifier: BSD-3-Clause +// https://github.com/AcademySoftwareFoundation/OpenShadingLanguage + + +surface +matte + [[ string description = "Lambertian diffuse material" ]] +( + float Kd = 1 + [[ string description = "Diffuse scaling", + float UImin = 0, float UIsoftmax = 1 ]], + color Cs = 1 + [[ string description = "Base color", + float UImin = 0, float UImax = 1 ]] + ) +{ + Ci = Kd * Cs * diffuse (N); +} diff --git a/testsuite/render-bunny/ref/out-macos-alt.exr b/testsuite/render-bunny/ref/out-macos-alt.exr new file mode 100644 index 000000000..e7be3a12b Binary files /dev/null and b/testsuite/render-bunny/ref/out-macos-alt.exr differ diff --git a/testsuite/render-bunny/ref/out.exr b/testsuite/render-bunny/ref/out.exr new file mode 100644 index 000000000..3a9608518 Binary files /dev/null and b/testsuite/render-bunny/ref/out.exr differ diff --git a/testsuite/render-bunny/run.py b/testsuite/render-bunny/run.py new file mode 100755 index 000000000..c82391707 --- /dev/null +++ b/testsuite/render-bunny/run.py @@ -0,0 +1,10 @@ +#!/usr/bin/env python + +# Copyright Contributors to the Open Shading Language project. +# SPDX-License-Identifier: BSD-3-Clause +# https://github.com/AcademySoftwareFoundation/OpenShadingLanguage + +failthresh = 0.01 +failpercent = 1 +outputs = [ "out.exr" ] +command = testrender("-r 256 256 -aa 8 bunny.xml out.exr") diff --git a/testsuite/render-cornell/NOOPTIMIZE b/testsuite/render-cornell/NOOPTIMIZE deleted file mode 100644 index 5036d37ee..000000000 --- a/testsuite/render-cornell/NOOPTIMIZE +++ /dev/null @@ -1 +0,0 @@ -Render too expensive without optimization diff --git a/testsuite/render-cornell/cornell.xml b/testsuite/render-cornell/cornell.xml index b94a93f8b..2247142e1 100644 --- a/testsuite/render-cornell/cornell.xml +++ b/testsuite/render-cornell/cornell.xml @@ -19,7 +19,7 @@ float eta 15; shader metal layer1; - float power 26000; shader emitter layer1 - + float power 26000; shader emitter layer1 + diff --git a/testsuite/render-cornell/ref/out.exr b/testsuite/render-cornell/ref/out.exr index 742b86972..005576713 100644 Binary files a/testsuite/render-cornell/ref/out.exr and b/testsuite/render-cornell/ref/out.exr differ diff --git a/testsuite/render-furnace-diffuse/NOOPTIMIZE b/testsuite/render-furnace-diffuse/NOOPTIMIZE deleted file mode 100644 index 5036d37ee..000000000 --- a/testsuite/render-furnace-diffuse/NOOPTIMIZE +++ /dev/null @@ -1 +0,0 @@ -Render too expensive without optimization diff --git a/testsuite/render-material-layer/NOOPTIMIZE b/testsuite/render-material-layer/NOOPTIMIZE deleted file mode 100644 index 5036d37ee..000000000 --- a/testsuite/render-material-layer/NOOPTIMIZE +++ /dev/null @@ -1 +0,0 @@ -Render too expensive without optimization diff --git a/testsuite/render-material-layer/material-layer.xml b/testsuite/render-material-layer/material-layer.xml index 0aad7c97a..390eb9769 100644 --- a/testsuite/render-material-layer/material-layer.xml +++ b/testsuite/render-material-layer/material-layer.xml @@ -48,13 +48,13 @@ - + - - - - + + + + diff --git a/testsuite/render-microfacet/NOOPTIMIZE b/testsuite/render-microfacet/NOOPTIMIZE deleted file mode 100644 index 5036d37ee..000000000 --- a/testsuite/render-microfacet/NOOPTIMIZE +++ /dev/null @@ -1 +0,0 @@ -Render too expensive without optimization diff --git a/testsuite/render-microfacet/ref/out-linux-alt.exr b/testsuite/render-microfacet/ref/out-linux-alt.exr index ed0fb8191..9f7e5bd1a 100644 Binary files a/testsuite/render-microfacet/ref/out-linux-alt.exr and b/testsuite/render-microfacet/ref/out-linux-alt.exr differ diff --git a/testsuite/render-microfacet/ref/out-macos-alt.exr b/testsuite/render-microfacet/ref/out-macos-alt.exr index 1d8f5b783..4458ee811 100644 Binary files a/testsuite/render-microfacet/ref/out-macos-alt.exr and b/testsuite/render-microfacet/ref/out-macos-alt.exr differ diff --git a/testsuite/render-microfacet/ref/out.exr b/testsuite/render-microfacet/ref/out.exr index 5b33f6026..6ec04c00d 100644 Binary files a/testsuite/render-microfacet/ref/out.exr and b/testsuite/render-microfacet/ref/out.exr differ diff --git a/testsuite/render-mx-burley-diffuse/NOOPTIMIZE b/testsuite/render-mx-burley-diffuse/NOOPTIMIZE deleted file mode 100644 index 5036d37ee..000000000 --- a/testsuite/render-mx-burley-diffuse/NOOPTIMIZE +++ /dev/null @@ -1 +0,0 @@ -Render too expensive without optimization diff --git a/testsuite/render-mx-burley-diffuse/ref/out-linux-alt.exr b/testsuite/render-mx-burley-diffuse/ref/out-linux-alt.exr new file mode 100644 index 000000000..8a462a919 Binary files /dev/null and b/testsuite/render-mx-burley-diffuse/ref/out-linux-alt.exr differ diff --git a/testsuite/render-mx-burley-diffuse/ref/out-macos-alt.exr b/testsuite/render-mx-burley-diffuse/ref/out-macos-alt.exr new file mode 100644 index 000000000..3beccc0aa Binary files /dev/null and b/testsuite/render-mx-burley-diffuse/ref/out-macos-alt.exr differ diff --git a/testsuite/render-mx-burley-diffuse/ref/out.exr b/testsuite/render-mx-burley-diffuse/ref/out.exr index 75cdb1928..f71c7bb12 100644 Binary files a/testsuite/render-mx-burley-diffuse/ref/out.exr and b/testsuite/render-mx-burley-diffuse/ref/out.exr differ diff --git a/testsuite/render-mx-conductor/NOOPTIMIZE b/testsuite/render-mx-conductor/NOOPTIMIZE deleted file mode 100644 index 5036d37ee..000000000 --- a/testsuite/render-mx-conductor/NOOPTIMIZE +++ /dev/null @@ -1 +0,0 @@ -Render too expensive without optimization diff --git a/testsuite/render-mx-conductor/ref/out-macos-alt.exr b/testsuite/render-mx-conductor/ref/out-macos-alt.exr new file mode 100644 index 000000000..933e7d860 Binary files /dev/null and b/testsuite/render-mx-conductor/ref/out-macos-alt.exr differ diff --git a/testsuite/render-mx-conductor/ref/out.exr b/testsuite/render-mx-conductor/ref/out.exr index 877554971..861f8d39a 100644 Binary files a/testsuite/render-mx-conductor/ref/out.exr and b/testsuite/render-mx-conductor/ref/out.exr differ diff --git a/testsuite/render-mx-dielectric-glass/NOOPTIMIZE b/testsuite/render-mx-dielectric-glass/NOOPTIMIZE deleted file mode 100644 index 5036d37ee..000000000 --- a/testsuite/render-mx-dielectric-glass/NOOPTIMIZE +++ /dev/null @@ -1 +0,0 @@ -Render too expensive without optimization diff --git a/testsuite/render-mx-dielectric-glass/ref/out.exr b/testsuite/render-mx-dielectric-glass/ref/out.exr index 1038a6705..9961a1b27 100644 Binary files a/testsuite/render-mx-dielectric-glass/ref/out.exr and b/testsuite/render-mx-dielectric-glass/ref/out.exr differ diff --git a/testsuite/render-mx-dielectric/NOOPTIMIZE b/testsuite/render-mx-dielectric/NOOPTIMIZE deleted file mode 100644 index 5036d37ee..000000000 --- a/testsuite/render-mx-dielectric/NOOPTIMIZE +++ /dev/null @@ -1 +0,0 @@ -Render too expensive without optimization diff --git a/testsuite/render-mx-dielectric/ref/out.exr b/testsuite/render-mx-dielectric/ref/out.exr index 083ea9a9e..1d5c2c259 100644 Binary files a/testsuite/render-mx-dielectric/ref/out.exr and b/testsuite/render-mx-dielectric/ref/out.exr differ diff --git a/testsuite/render-mx-furnace-burley-diffuse/NOOPTIMIZE b/testsuite/render-mx-furnace-burley-diffuse/NOOPTIMIZE deleted file mode 100644 index 5036d37ee..000000000 --- a/testsuite/render-mx-furnace-burley-diffuse/NOOPTIMIZE +++ /dev/null @@ -1 +0,0 @@ -Render too expensive without optimization diff --git a/testsuite/render-mx-furnace-burley-diffuse/ref/out.exr b/testsuite/render-mx-furnace-burley-diffuse/ref/out.exr index ee615348c..c8a36c3f7 100644 Binary files a/testsuite/render-mx-furnace-burley-diffuse/ref/out.exr and b/testsuite/render-mx-furnace-burley-diffuse/ref/out.exr differ diff --git a/testsuite/render-mx-furnace-burley-diffuse/scene.xml b/testsuite/render-mx-furnace-burley-diffuse/scene.xml index faf609c9b..341c85cb7 100644 --- a/testsuite/render-mx-furnace-burley-diffuse/scene.xml +++ b/testsuite/render-mx-furnace-burley-diffuse/scene.xml @@ -1,6 +1,6 @@ - + - float power 160000; shader emitter layer1; - + float power 200000; shader emitter layer1; + diff --git a/testsuite/render-raytypes/ref/out-macos-alt.exr b/testsuite/render-raytypes/ref/out-macos-alt.exr new file mode 100644 index 000000000..997991f35 Binary files /dev/null and b/testsuite/render-raytypes/ref/out-macos-alt.exr differ diff --git a/testsuite/render-raytypes/ref/out.exr b/testsuite/render-raytypes/ref/out.exr index 08379e170..73e085d74 100644 Binary files a/testsuite/render-raytypes/ref/out.exr and b/testsuite/render-raytypes/ref/out.exr differ diff --git a/testsuite/render-uv/NOOPTIMIZE b/testsuite/render-uv/NOOPTIMIZE deleted file mode 100644 index 5036d37ee..000000000 --- a/testsuite/render-uv/NOOPTIMIZE +++ /dev/null @@ -1 +0,0 @@ -Render too expensive without optimization diff --git a/testsuite/render-uv/ref/out.exr b/testsuite/render-uv/ref/out.exr index f371ebdc9..f2ae4cab9 100644 Binary files a/testsuite/render-uv/ref/out.exr and b/testsuite/render-uv/ref/out.exr differ diff --git a/testsuite/render-uv/run.py b/testsuite/render-uv/run.py index 490d13864..0af3895ea 100755 --- a/testsuite/render-uv/run.py +++ b/testsuite/render-uv/run.py @@ -10,4 +10,4 @@ idiff_program = "idiff" outputs = [ "out.exr" ] -command = testrender("-r 320 240 -aa 8 scene.xml out.exr") +command = testrender("-r 320 240 -aa 16 scene.xml out.exr") diff --git a/testsuite/render-uv/scene.xml b/testsuite/render-uv/scene.xml index 329257f54..097328129 100644 --- a/testsuite/render-uv/scene.xml +++ b/testsuite/render-uv/scene.xml @@ -17,7 +17,7 @@ - float power 160000; shader emitter layer1; - + float power 200000; shader emitter layer1; + diff --git a/testsuite/render-veachmis/NOOPTIMIZE b/testsuite/render-veachmis/NOOPTIMIZE deleted file mode 100644 index 5036d37ee..000000000 --- a/testsuite/render-veachmis/NOOPTIMIZE +++ /dev/null @@ -1 +0,0 @@ -Render too expensive without optimization diff --git a/testsuite/render-veachmis/ref/out-linux-alt.exr b/testsuite/render-veachmis/ref/out-linux-alt.exr deleted file mode 100644 index 27e12f21b..000000000 Binary files a/testsuite/render-veachmis/ref/out-linux-alt.exr and /dev/null differ diff --git a/testsuite/render-veachmis/ref/out-macos-alt.exr b/testsuite/render-veachmis/ref/out-macos-alt.exr new file mode 100644 index 000000000..391100a80 Binary files /dev/null and b/testsuite/render-veachmis/ref/out-macos-alt.exr differ diff --git a/testsuite/render-veachmis/ref/out.exr b/testsuite/render-veachmis/ref/out.exr index 1823fb0c8..797731976 100644 Binary files a/testsuite/render-veachmis/ref/out.exr and b/testsuite/render-veachmis/ref/out.exr differ diff --git a/testsuite/render-veachmis/run.py b/testsuite/render-veachmis/run.py index 8805e28af..0f2a25d8c 100755 --- a/testsuite/render-veachmis/run.py +++ b/testsuite/render-veachmis/run.py @@ -5,4 +5,4 @@ # https://github.com/AcademySoftwareFoundation/OpenShadingLanguage outputs = [ "out.exr" ] -command = testrender("-r 320 240 -aa 4 veach.xml out.exr") +command = testrender("-r 320 240 -aa 16 veach.xml out.exr") diff --git a/testsuite/render-veachmis/veach.xml b/testsuite/render-veachmis/veach.xml index a0beac1e8..7da069b03 100644 --- a/testsuite/render-veachmis/veach.xml +++ b/testsuite/render-veachmis/veach.xml @@ -45,10 +45,10 @@ - float power 2000; shader emitter layer1; - - - - + float power 8000; shader emitter layer1; + + + + diff --git a/testsuite/render-ward/NOOPTIMIZE b/testsuite/render-ward/NOOPTIMIZE deleted file mode 100644 index 5036d37ee..000000000 --- a/testsuite/render-ward/NOOPTIMIZE +++ /dev/null @@ -1 +0,0 @@ -Render too expensive without optimization diff --git a/testsuite/render-ward/ref/out.exr b/testsuite/render-ward/ref/out.exr index 25585daca..20f6255a0 100644 Binary files a/testsuite/render-ward/ref/out.exr and b/testsuite/render-ward/ref/out.exr differ diff --git a/testsuite/render-ward/scene.xml b/testsuite/render-ward/scene.xml index e21862406..405f1ce5e 100644 --- a/testsuite/render-ward/scene.xml +++ b/testsuite/render-ward/scene.xml @@ -36,7 +36,7 @@ - float power 80000; shader emitter layer1; - + float power 101000; shader emitter layer1; +