├── README.md ├── src └── carver.cpp ├── include ├── precision.hpp ├── external.hpp ├── utils.hpp ├── debug.hpp ├── imutils.hpp └── vec3.hpp ├── CMakeLists.txt ├── .gitignore └── LICENSE /README.md: -------------------------------------------------------------------------------- 1 | # point-carver 2 | Seam Marker with Points in modern C++ 3 | -------------------------------------------------------------------------------- /src/carver.cpp: -------------------------------------------------------------------------------- 1 | // carver program entry point 2 | #include 3 | 4 | int main() { 5 | std::cout << "Hello Point Carver" << std::endl; 6 | } 7 | -------------------------------------------------------------------------------- /include/precision.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace pcarver { 6 | // typedef float real; 7 | typedef double real; 8 | typedef unsigned int col; 9 | typedef unsigned int row; 10 | 11 | typedef std::vector> smat; 12 | }; 13 | -------------------------------------------------------------------------------- /include/external.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | // thirdparty libs 11 | #include 12 | 13 | // 14 | #define STB_IMAGE_IMPLEMENTATION 15 | #include 16 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.0.2) 2 | project("PointCarverCpp") 3 | 4 | #find_package(OpenGL REQUIRED) 5 | find_package (Threads REQUIRED) 6 | 7 | set (CMAKE_BUILD_TYPE "Debug") 8 | set (CMAKE_CXX_FLAGS "-std=c++17") 9 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread -ggdb -Wall -Wextra") 10 | 11 | include_directories( 12 | "./include/" 13 | ) 14 | 15 | add_executable(carver.out "src/carver.cpp") 16 | 17 | install(TARGETS carver.out DESTINATION "${PROJECT_SOURCE_DIR}/bin/") 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | # cmake gitignore 35 | CMakeLists.txt.user 36 | CMakeCache.txt 37 | CMakeFiles 38 | CMakeScripts 39 | Testing 40 | Makefile 41 | cmake_install.cmake 42 | install_manifest.txt 43 | compile_commands.json 44 | CTestTestfile.cmake 45 | _deps 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 DKE 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /include/utils.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace pcarver { 7 | 8 | /** 9 | \brief transform degree to radian 10 | */ 11 | inline real degrees_to_radians(real degrees) { 12 | return degrees * M_PI / 180.0; 13 | } 14 | /** 15 | \brief transform radian to degrees 16 | */ 17 | inline real radians_to_degrees(real radian) { 18 | return radian * 180.0 / M_PI; 19 | } 20 | 21 | /** 22 | \brief clamp value to given range 23 | \param x value to be clamped 24 | \param min minimum value of the range 25 | \param max maximum value of the range 26 | 27 | \return either x, min or the max 28 | */ 29 | template 30 | T clamp(T x, U min, V max) { 31 | if (x < min) 32 | return min; 33 | if (x > max) 34 | return max; 35 | return x; 36 | } 37 | 38 | /** 39 | \brief clamp value to given range 40 | \param x value to be clamped 41 | \param min minimum value of the range 42 | \param max maximum value of the range 43 | 44 | \return either x, min or the max all in the same type 45 | */ 46 | template T dclamp(T x, T mn, T mx) { 47 | return clamp(x, mn, mx); 48 | } 49 | 50 | /** 51 | \brief even spaced vector between given range 52 | 53 | \param start start of the range 54 | \param end end of the range 55 | \param size of the resulting vector 56 | 57 | \return lspaced evenly spaced vector 58 | */ 59 | template 60 | std::vector linspace(T start, T end, uint size) { 61 | // from: https://stackoverflow.com/a/27030598/7330813 62 | std::vector lspaced; 63 | if (size == 0) 64 | return lspaced; 65 | if (size == 1) { 66 | lspaced.push_back(start); 67 | return lspaced; 68 | } 69 | auto delta = (end - start) / (size - 1); 70 | for (unsigned int i = 0; i < size - 1; i++) { 71 | lspaced.push_back(start + delta * i); 72 | } 73 | lspaced.push_back(end); 74 | return lspaced; 75 | } 76 | 77 | /** 78 | \brief cast vector values to DestType type 79 | 80 | \param vs vector whose values are going to be cast 81 | \param fn cast, transform function 82 | 83 | \return ds vector in DestType 84 | */ 85 | template 86 | inline std::vector 87 | cast_vec(const std::vector &vs, 88 | const std::function &fn) { 89 | std::vector ds; 90 | ds.resize(vs.size()); 91 | for (std::size_t i = 0; i < vs.size(); i++) { 92 | ds[i] = fn(vs[i]); 93 | } 94 | return ds; 95 | } 96 | 97 | /** 98 | \brief cast vector values to DestType type 99 | 100 | \param vs vector whose values are going to be cast 101 | \param fn cast, transform function 102 | 103 | \return vector in DestType 104 | */ 105 | template 106 | inline std::vector 107 | cast_vec(const std::vector &vs) { 108 | return cast_vec( 109 | vs, [](auto i) { return static_cast(i); }); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /include/debug.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace pcarver { 4 | 5 | #define D_CHECK(call) \ 6 | do { \ 7 | bool res = call; \ 8 | if (!res) { \ 9 | std::stringstream txt; \ 10 | txt << #call << " :: " \ 11 | << "false" \ 12 | << " :: " << __FILE__ << " :: " << __LINE__ \ 13 | << std::end; \ 14 | throw std::runtime_error(txt.str()); \ 15 | } \ 16 | } while (0) 17 | 18 | #define D_CHECK_MSG(call, msg) \ 19 | do { \ 20 | bool res = call; \ 21 | if (!res) { \ 22 | std::stringstream txt; \ 23 | txt << #call << " :: " \ 24 | << "false" \ 25 | << " :: " << __FILE__ << " :: " << __LINE__ \ 26 | << std::endl \ 27 | << msg << std::endl; \ 28 | throw std::runtime_error(txt.str()); \ 29 | } \ 30 | } while (0) 31 | 32 | #define COMP_CHECK(call, el1, el2) \ 33 | do { \ 34 | bool res = call; \ 35 | auto el1v = el1; \ 36 | auto el2v = el2; \ 37 | if (!res) { \ 38 | std::stringstream txt; \ 39 | txt << #call << " values: " << std::endl \ 40 | << #el1 << ": " << el1v << std::endl \ 41 | << #el2 << ": " << el2v << std::endl \ 42 | << " :: " \ 43 | << "false" \ 44 | << " :: " << __FILE__ << " :: " << __LINE__ \ 45 | << std::endl; \ 46 | throw std::runtime_error(txt.str()); \ 47 | } \ 48 | } while (0) 49 | #define COMP_CHECK_MSG(call, el1, el2, msg) \ 50 | do { \ 51 | bool res = call; \ 52 | auto el1v = el1; \ 53 | auto el2v = el2; \ 54 | if (!res) { \ 55 | std::stringstream txt; \ 56 | txt << #call << " values: " << std::endl \ 57 | << #el1 << ": " << el1v << std::endl \ 58 | << #el2 << ": " << el2v << std::endl \ 59 | << " :: " \ 60 | << "false" \ 61 | << " :: " << __FILE__ << " :: " << __LINE__ \ 62 | << " :: " << msg << std::endl; \ 63 | throw std::runtime_error(txt.str()); \ 64 | } \ 65 | } while (0) 66 | }; 67 | -------------------------------------------------------------------------------- /include/imutils.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | // image utilities 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | using namespace pcarver; 9 | 10 | namespace pcarver { 11 | 12 | typedef std::vector> immat; 13 | 14 | struct imcoord { 15 | row y; 16 | col x; 17 | }; 18 | 19 | immat read_image(const std::string &img_path) { 20 | int w; 21 | int h; 22 | int nb_channels; 23 | unsigned char *data = 24 | stbi_load(img_path.c_str(), &w, &h, &nb_channels, 0); 25 | row height = static_cast(h); 26 | col width = static_cast(w); 27 | unsigned int bytes_per_scanline = nb_channels * width; 28 | immat img; 29 | for (row i = 0; i < height; i++) { 30 | std::vector row_vec; 31 | row_vec.resize(width); 32 | img.push_back(row_vec); 33 | for (col j = 0; j < width; j++) { 34 | auto pixel = 35 | data + i * bytes_per_scanline + j * nb_channels; 36 | tricolor tcolor(pixel[0], pixel[1], pixel[2]); 37 | img[i][j] = tcolor; 38 | } 39 | } 40 | return img; 41 | } 42 | 43 | struct ImgRect { 44 | immat pixels; 45 | imcoord bottom_left; 46 | ImgRect(const imcoord &coords, const immat &pixs) 47 | : pixels(pixs), bottom_left(coords) {} 48 | ImgRect(const std::string &impath) { 49 | pixels = read_image(impath); 50 | imcoord coord; 51 | coord.y = pixels.size(); 52 | coord.x = 0; 53 | bottom_left = coord; 54 | } 55 | std::vector get_row(row index) const { 56 | 57 | COMP_CHECK_MSG(index > 0 || index < pixels.size(), 58 | index, pixels.size(), 59 | "given row index is out of bounds"); 60 | return pixels[index]; 61 | } 62 | std::vector get_col(col index) const { 63 | 64 | COMP_CHECK_MSG(index > 0 || index < pixels[0].size(), 65 | index, pixels[0].size(), 66 | "given column index is out of bounds"); 67 | std::vector imcol(0, pixels.size()); 68 | for (row i = 0; i < pixels.size(); i++) { 69 | auto rvec = pixels[i]; 70 | imcol[i] = rvec[index]; 71 | } 72 | return imcol; 73 | } 74 | tricolor get_pixel(row y, col x) const { 75 | auto rvec = get_row(y); 76 | COMP_CHECK_MSG(x > 0 || x < rvec.size(), x, rvec.size(), 77 | "given column value is out of bounds"); 78 | return rvec[x]; 79 | } 80 | tricolor get_pixel(const imcoord &coord) const { 81 | return get_pixel(coord.y, coord.x); 82 | } 83 | real pixel_sum(row y, col x) const { 84 | auto pix = get_pixel(y, x); 85 | return pix.sum(); 86 | } 87 | real sum() const { 88 | real s = 0; 89 | for (const auto &rvec : pixels) { 90 | for (const auto &cval : rvec) { 91 | s += cval.sum(); 92 | } 93 | } 94 | return s; 95 | } 96 | 97 | std::vector rowsum() const { 98 | std::vector row_vec(0, pixels.size()); 99 | for (row i = 0; i < pixels.size(); i++) { 100 | const auto &rvec = pixels[i]; 101 | tricolor row_counter(0.0); 102 | for (const auto &cval : rvec) { 103 | row_counter += cval; 104 | } 105 | row_vec[i] = row_counter; 106 | } 107 | return row_vec; 108 | } 109 | std::vector colsum() const { 110 | std::vector col_vec(0, pixels[0].size()); 111 | for (col j = 0; j < pixels[0].size(); j++) { 112 | tricolor col_counter(0.0); 113 | for (const auto &rvec : pixels) { 114 | col_counter += rvec[j]; 115 | } 116 | col_vec[j] = col_counter; 117 | } 118 | return col_vec; 119 | } 120 | /**returns maximum pixel value in the image part*/ 121 | tricolor max(row &y, col &x) { 122 | real temp = FLT_MIN; 123 | tricolor tval(0.0); 124 | for (row i = 0; i < pixels.size(); i++) { 125 | for (col j = 0; j < pixels[0].size(); j++) { 126 | tricolor cval = get_pixel(i, j); 127 | auto ctemp = cval.sum(); 128 | if (ctemp > temp) { 129 | temp = ctemp; 130 | y = i; 131 | x = j; 132 | tval = cval; 133 | } 134 | } 135 | } 136 | return tval; 137 | } 138 | /**returns minimum pixel value in the image part*/ 139 | tricolor min(row &y, col &x) { 140 | real temp = FLT_MAX; 141 | tricolor tval(0.0); 142 | for (row i = 0; i < pixels.size(); i++) { 143 | for (col j = 0; j < pixels[0].size(); j++) { 144 | tricolor cval = get_pixel(i, j); 145 | auto ctemp = cval.sum(); 146 | if (ctemp < temp) { 147 | temp = ctemp; 148 | y = i; 149 | x = j; 150 | tval = cval; 151 | } 152 | } 153 | } 154 | return tval; 155 | } 156 | tricolor min() { 157 | row y = 0; 158 | col x = 0; 159 | return min(y, x); 160 | } 161 | tricolor max() { 162 | row y = 0; 163 | col x = 0; 164 | return max(y, x); 165 | } 166 | tricolor min(imcoord &coords) { 167 | row y = 0; 168 | col x = 0; 169 | auto val = min(y, x); 170 | coords.y = y; 171 | coords.x = x; 172 | return val; 173 | } 174 | tricolor max(imcoord &coords) { 175 | row y = 0; 176 | col x = 0; 177 | auto val = max(y, x); 178 | coords.y = y; 179 | coords.x = x; 180 | return val; 181 | } 182 | unsigned int nb_cols() const { return pixels[0].size(); } 183 | unsigned int nb_rows() const { return pixels.size(); } 184 | unsigned int nb_pixels() const { 185 | return nb_rows() * nb_cols(); 186 | } 187 | unsigned int size() const { return nb_pixels(); } 188 | unsigned int width() const { return nb_cols(); } 189 | unsigned int height() const { return nb_rows(); } 190 | }; 191 | }; 192 | -------------------------------------------------------------------------------- /include/vec3.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | // 3 | #include 4 | #include 5 | #include 6 | 7 | using namespace pcarver; 8 | namespace pcarver { 9 | 10 | class vec3 { 11 | public: 12 | vec3() : e{0, 0, 0} {} 13 | vec3(real e0, real e1, real e2) : e{e0, e1, e2} {} 14 | vec3(real e0) : e{e0, e0, e0} {} 15 | vec3(const real e1[3]) : e{e1[0], e1[1], e1[2]} {} 16 | 17 | real x() const { return e[0]; } 18 | real y() const { return e[1]; } 19 | real z() const { return e[2]; } 20 | real r() const { return x(); } 21 | real g() const { return y(); } 22 | real b() const { return z(); } 23 | real sum() const { return z() + x() + y(); } 24 | 25 | vec3 operator-() const { 26 | return vec3(-e[0], -e[1], -e[2]); 27 | } 28 | real operator[](int i) const { return e[i]; } 29 | real &operator[](int i) { return e[i]; } 30 | vec3 &operator+=(const vec3 &v) { 31 | e[0] += v.e[0]; 32 | e[1] += v.e[1]; 33 | e[2] += v.e[2]; 34 | return *this; 35 | } 36 | vec3 &operator*=(const real t) { 37 | e[0] *= t; 38 | e[1] *= t; 39 | e[2] *= t; 40 | return *this; 41 | } 42 | vec3 &operator/=(const real t) { return *this *= 1 / t; } 43 | vec3 add(const vec3 &v) { 44 | return vec3(x() + v.x(), y() + v.y(), z() + v.z()); 45 | } 46 | real sum() { return x() + y() + z(); } 47 | real average() { return sum() / 3.0; } 48 | vec3 add(const real &v) { 49 | return vec3(x() + v, y() + v, z() + v); 50 | } 51 | vec3 subt(const real &v) { 52 | return vec3(x() - v, y() - v, z() - v); 53 | } 54 | vec3 subt(const vec3 &v) { 55 | return vec3(x() - v.x(), y() - v.y(), z() - v.z()); 56 | } 57 | vec3 multip(const vec3 &v) { 58 | return vec3(x() * v.x(), y() * v.y(), z() * v.z()); 59 | } 60 | vec3 multip(const real &v) { 61 | return vec3(x() * v, y() * v, z() * v); 62 | } 63 | vec3 div(const real &v) { 64 | if (v == 0.0) 65 | throw std::runtime_error("no zero division"); 66 | return vec3(x() / v, y() / v, z() / v); 67 | } 68 | vec3 div(const vec3 &v) { 69 | if (v.x() == 0.0) 70 | throw std::runtime_error( 71 | "no zero division x is zero"); 72 | if (v.y() == 0.0) 73 | throw std::runtime_error( 74 | "no zero division y is zero"); 75 | if (v.z() == 0.0) 76 | throw std::runtime_error( 77 | "no zero division z is zero"); 78 | return vec3(x() / v.x(), y() / v.y(), z() / v.z()); 79 | } 80 | real length() const { return sqrt(length_squared()); } 81 | real length_squared() const { 82 | return e[0] * e[0] + e[1] * e[1] + e[2] * e[2]; 83 | } 84 | inline real min() const { 85 | return fmin(fmin(x(), y()), z()); 86 | } 87 | inline real max() const { 88 | return fmax(fmax(x(), y()), z()); 89 | } 90 | bool near_zero() const { 91 | // Return true if the vector is close to zero in all 92 | // dimensions. 93 | const auto s = 1e-8; 94 | return (fabs(e[0]) < s) && (fabs(e[1]) < s) && 95 | (fabs(e[2]) < s); 96 | } 97 | 98 | std::vector to_vector() const { 99 | std::vector vdata(3); 100 | vdata[0] = x(); 101 | vdata[1] = y(); 102 | vdata[2] = z(); 103 | return vdata; 104 | } 105 | void conditional_set( 106 | real setval, int index, 107 | const std::function &fn) { 108 | if (fn(e[index])) { 109 | e[index] = setval; 110 | } 111 | } 112 | void conditional_set( 113 | real setval, 114 | const std::function &fn) { 115 | for (int index = 0; index < 3; index++) { 116 | if (fn(e[index])) { 117 | e[index] = setval; 118 | } 119 | } 120 | } 121 | 122 | public: 123 | real e[3]; 124 | }; 125 | 126 | // Type aliases for vec3 127 | using point3 = vec3; // 3D point 128 | using tricolor = vec3; // 3d color 129 | 130 | // vec3 Utility Functions 131 | 132 | inline std::ostream &operator<<(std::ostream &out, 133 | const vec3 &v) { 134 | return out << v.e[0] << ' ' << v.e[1] << ' ' << v.e[2]; 135 | } 136 | inline vec3 operator+(const vec3 &u, const vec3 &v) { 137 | return vec3(u.e[0] + v.e[0], u.e[1] + v.e[1], 138 | u.e[2] + v.e[2]); 139 | } 140 | inline vec3 operator-(const vec3 &u, const vec3 &v) { 141 | return vec3(u.e[0] - v.e[0], u.e[1] - v.e[1], 142 | u.e[2] - v.e[2]); 143 | } 144 | inline vec3 operator*(const vec3 &u, const vec3 &v) { 145 | return vec3(u.e[0] * v.e[0], u.e[1] * v.e[1], 146 | u.e[2] * v.e[2]); 147 | } 148 | inline vec3 operator*(real t, const vec3 &v) { 149 | return vec3(t * v.e[0], t * v.e[1], t * v.e[2]); 150 | } 151 | inline vec3 operator*(const vec3 &v, real t) { 152 | return t * v; 153 | } 154 | inline vec3 operator/(vec3 v, real t) { 155 | return (1 / t) * v; 156 | } 157 | inline vec3 operator/(real t, vec3 v) { 158 | return (1 / t) * v; 159 | } 160 | inline vec3 operator/(vec3 v, vec3 t) { 161 | return (1 / t) * v; 162 | } 163 | 164 | inline real dot(const vec3 &u, const vec3 &v) { 165 | return u.e[0] * v.e[0] + u.e[1] * v.e[1] + 166 | u.e[2] * v.e[2]; 167 | } 168 | vec3 sqrt_vec(const vec3 &v) { 169 | auto r = sqrt(v.x()); 170 | auto g = sqrt(v.y()); 171 | auto b = sqrt(v.z()); 172 | vec3 rgb = vec3(r, g, b); 173 | return rgb; 174 | } 175 | vec3 clamp(const vec3 &v, real mn, real mx) { 176 | auto r = clamp(v.x(), mn, mx); 177 | auto g = clamp(v.y(), mn, mx); 178 | auto b = clamp(v.z(), mn, mx); 179 | return vec3(r, g, b); 180 | } 181 | inline vec3 cross(const vec3 &u, const vec3 &v) { 182 | return vec3(u.e[1] * v.e[2] - u.e[2] * v.e[1], 183 | u.e[2] * v.e[0] - u.e[0] * v.e[2], 184 | u.e[0] * v.e[1] - u.e[1] * v.e[0]); 185 | } 186 | 187 | inline vec3 unit_vector(vec3 v) { return v / v.length(); } 188 | inline vec3 min_vec(const vec3 &v1, const vec3 &v2) { 189 | float x = fmin(v1.x(), v2.x()); 190 | float y = fmin(v1.y(), v2.y()); 191 | float z = fmin(v1.z(), v2.z()); 192 | return vec3(x, y, z); 193 | } 194 | inline vec3 max_vec(const vec3 &v1, const vec3 &v2) { 195 | float x = fmax(v1.x(), v2.x()); 196 | float y = fmax(v1.y(), v2.y()); 197 | float z = fmax(v1.z(), v2.z()); 198 | return vec3(x, y, z); 199 | } 200 | 201 | inline vec3 reflect(const vec3 &v, const vec3 &n) { 202 | return v - 2 * dot(v, n) * n; 203 | } 204 | 205 | inline vec3 refract(const vec3 &uv, const vec3 &n, 206 | real etai_over_etat) { 207 | auto cos_theta = fmin(dot(-uv, n), 1.0); 208 | vec3 r_out_perp = etai_over_etat * (uv + cos_theta * n); 209 | vec3 r_out_parallel = 210 | -sqrt(fabs(1.0 - r_out_perp.length_squared())) * n; 211 | return r_out_perp + r_out_parallel; 212 | } 213 | } 214 | --------------------------------------------------------------------------------