diff --git a/README.md b/README.md index e714190..12f498b 100644 --- a/README.md +++ b/README.md @@ -128,9 +128,19 @@ Several examples can be watched [HERE](https://youtu.be/pbLVMDj1Zro). ### Windows -Ready to go. The compilation is not required. Use the binary file `bcpd.exe` in the `win` directory. -The binary file was created by GCC included in the 32-bit version of the MinGW system. -Therefore, it might be quite slower than the one compiled in a Mac/Linux system. +Ready to go. The compilation is not required. Use the binary file `bcpd.exe` or `gbcpd.exe` in the `win` directory. + +- `bcpd.exe`: Original BCPD executable (32-bit) +- `gbcpd.exe`: Enhanced BCPD executable with PLY support (64-bit) + +The `bcpd.exe` binary file was created by GCC included in the 32-bit version of the MinGW system. +The `gbcpd.exe` binary file was created using 64-bit MinGW cross-compiler and includes support for PLY file format. + +If you want to compile the Windows version yourself, you can use the following command in a Linux environment with MinGW installed: + +```bash +make -f makefile.win +``` ### MacOS and Linux @@ -154,6 +164,14 @@ For Windows, type the following command in the DOS prompt: ` bcpd -x -y (+options) ` +Or, if using the enhanced version with PLY support: + +` gbcpd -t -s -o (+options) ` + +The enhanced version also supports a simplified command format: + +` gbcpd ` + Brief instructions are printed by typing `./bcpd -v` (or `bcpd -v` for windows) in the terminal window. The binary file can also be executed using the `system` function in MATLAB. See MATLAB scripts in the `demo` folder regarding the usage of the binary file. @@ -170,6 +188,9 @@ See MATLAB scripts in the `demo` folder regarding the usage of the binary file. - `-x [file]`: The target shape represented as a matrix of size N x D. - `-y [file]`: The source shape represented as a matrix of size M x D. +- `-t [file]`: (Enhanced version) The target shape in PLY format. +- `-s [file]`: (Enhanced version) The source shape in PLY format. +- `-o [prefix]`: (Enhanced version) Output file prefix. Tab- and comma-separated files are accepted, and the extensions of input files MUST be `.txt`. If your file is space-delimited, convert it to a tab- or comma-separated file using Excel, diff --git a/base/dummy_lapack.c b/base/dummy_lapack.c new file mode 100644 index 0000000..b750c5e --- /dev/null +++ b/base/dummy_lapack.c @@ -0,0 +1,55 @@ +#include +#include + +#ifdef USE_DUMMY_LAPACK + +// Dummy LAPACK functions +int dgesv_(int *n, int *nrhs, double *a, int *lda, int *ipiv, double *b, int *ldb, int *info) { + *info = -1; // Indicate error + fprintf(stderr, "Error: LAPACK function dgesv_ not available in this build.\n"); + return -1; +} + +int dsyev_(char *jobz, char *uplo, int *n, double *a, int *lda, double *w, double *work, int *lwork, int *info) { + *info = -1; // Indicate error + fprintf(stderr, "Error: LAPACK function dsyev_ not available in this build.\n"); + return -1; +} + +int dgesvd_(char *jobu, char *jobvt, int *m, int *n, double *a, int *lda, double *s, double *u, int *ldu, double *vt, int *ldvt, double *work, int *lwork, int *info) { + *info = -1; // Indicate error + fprintf(stderr, "Error: LAPACK function dgesvd_ not available in this build.\n"); + return -1; +} + +int dposv_(char *uplo, int *n, int *nrhs, double *A, int *lda, double *B, int *ldb, int *info) { + *info = -1; // Indicate error + fprintf(stderr, "Error: LAPACK function dposv_ not available in this build.\n"); + return -1; +} + +int dpotrs_(char *uplo, int *n, int *nrhs, double *A, int *lda, double *B, int *ldb, int *info) { + *info = -1; // Indicate error + fprintf(stderr, "Error: LAPACK function dpotrs_ not available in this build.\n"); + return -1; +} + +int dpotrf_(char *uplo, int *n, double *A, int *lda, int *info) { + *info = -1; // Indicate error + fprintf(stderr, "Error: LAPACK function dpotrf_ not available in this build.\n"); + return -1; +} + +int dpotri_(char *uplo, int *n, double *A, int *lda, int *info) { + *info = -1; // Indicate error + fprintf(stderr, "Error: LAPACK function dpotri_ not available in this build.\n"); + return -1; +} + +int dgetrf_(int *m, int *n, double *A, int *lda, int *ipiv, int *info) { + *info = -1; // Indicate error + fprintf(stderr, "Error: LAPACK function dgetrf_ not available in this build.\n"); + return -1; +} + +#endif // USE_DUMMY_LAPACK \ No newline at end of file diff --git a/base/dummy_lapack.h b/base/dummy_lapack.h new file mode 100644 index 0000000..75c8b40 --- /dev/null +++ b/base/dummy_lapack.h @@ -0,0 +1,38 @@ +// Copyright (c) 2023 +// +// 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 DUMMY_LAPACK_H +#define DUMMY_LAPACK_H + +#ifdef USE_DUMMY_LAPACK + +// Function declarations for dummy LAPACK functions +int dgesv_(int *n, int *nrhs, double *a, int *lda, int *ipiv, double *b, int *ldb, int *info); +int dsyev_(char *jobz, char *uplo, int *n, double *a, int *lda, double *w, double *work, int *lwork, int *info); +int dgesvd_(char *jobu, char *jobvt, int *m, int *n, double *a, int *lda, double *s, double *u, int *ldu, double *vt, int *ldvt, double *work, int *lwork, int *info); +int dposv_(char *uplo, int *n, int *nrhs, double *A, int *lda, double *B, int *ldb, int *info); +int dpotrs_(char *uplo, int *n, int *nrhs, double *A, int *lda, double *B, int *ldb, int *info); +int dpotrf_(char *uplo, int *n, double *A, int *lda, int *info); +int dpotri_(char *uplo, int *n, double *A, int *lda, int *info); +int dgetrf_(int *m, int *n, double *A, int *lda, int *ipiv, int *info); + +#endif // USE_DUMMY_LAPACK + +#endif // DUMMY_LAPACK_H \ No newline at end of file diff --git a/base/getopt.c b/base/getopt.c new file mode 100644 index 0000000..2dad30b --- /dev/null +++ b/base/getopt.c @@ -0,0 +1,52 @@ +// getopt.c for Windows +#include +#include + +char *optarg; +int optind = 1; +int opterr = 1; +int optopt; + +int getopt(int argc, char *const argv[], const char *optstring) { + static int sp = 1; + int c; + char *cp; + + if (sp == 1) { + if (optind >= argc || argv[optind][0] != '-' || argv[optind][1] == '\0') + return -1; + else if (strcmp(argv[optind], "--") == 0) { + optind++; + return -1; + } + } + + optopt = c = argv[optind][sp]; + + if (c == ':' || (cp = strchr(optstring, c)) == NULL) { + if (argv[optind][++sp] == '\0') { + optind++; + sp = 1; + } + return '?'; + } + + if (*(cp + 1) == ':') { + if (argv[optind][sp + 1] != '\0') + optarg = &argv[optind++][sp + 1]; + else if (++optind >= argc) { + sp = 1; + return '?'; + } else + optarg = argv[optind++]; + sp = 1; + } else { + if (argv[optind][++sp] == '\0') { + sp = 1; + optind++; + } + optarg = NULL; + } + + return c; +} \ No newline at end of file diff --git a/base/getopt.h b/base/getopt.h new file mode 100644 index 0000000..81fa547 --- /dev/null +++ b/base/getopt.h @@ -0,0 +1,20 @@ +// getopt.h for Windows +#ifndef GETOPT_H +#define GETOPT_H + +#ifdef __cplusplus +extern "C" { +#endif + +extern char *optarg; +extern int optind; +extern int opterr; +extern int optopt; + +int getopt(int argc, char *const argv[], const char *optstring); + +#ifdef __cplusplus +} +#endif + +#endif /* GETOPT_H */ \ No newline at end of file diff --git a/base/plyreader.c b/base/plyreader.c new file mode 100644 index 0000000..e57d59c --- /dev/null +++ b/base/plyreader.c @@ -0,0 +1,117 @@ +// Copyright (c) 2023 +// +// 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. + +#include +#include +#include +#include +#include "rply/rply.h" +#include "util.h" +#include "plyreader.h" + +// Structure to hold vertex data during PLY parsing +typedef struct { + double **vertices; // Array to store vertex coordinates + int vertex_count; // Total number of vertices + int current_vertex; // Current vertex being processed + int dimension; // Dimension of vertices (usually 3) +} ply_vertex_data; + +// Callback function for vertex elements +static int vertex_cb(p_ply_argument argument) { + long coord; + ply_vertex_data *data; + + // Get user data and which coordinate (x, y, z) we're processing + ply_get_argument_user_data(argument, (void **)&data, &coord); + + // Get the value of the coordinate + double value = ply_get_argument_value(argument); + + // Store the value in our vertex array + if (data->current_vertex < data->vertex_count && coord < data->dimension) { + data->vertices[data->current_vertex][coord] = value; + + // If we've processed all coordinates for this vertex, move to the next + if (coord == data->dimension - 1) { + data->current_vertex++; + } + } + + return 1; +} + +// Function to read PLY file and return vertices as a 2D array +double **read_ply(int *nr, int *nc, const char *file, const char *na) { + p_ply ply; + ply_vertex_data data; + long nvertices; + int i; + + // Open PLY file + ply = ply_open(file, NULL, 0, NULL); + if (!ply) { + printf("ERROR: Failed to open PLY file: %s\n", file); + exit(EXIT_FAILURE); + } + + // Read PLY header + if (!ply_read_header(ply)) { + printf("ERROR: Failed to read PLY header from file: %s\n", file); + ply_close(ply); + exit(EXIT_FAILURE); + } + + // Get number of vertices + nvertices = ply_set_read_cb(ply, "vertex", "x", NULL, NULL, 0); + if (nvertices <= 0) { + printf("ERROR: No vertices found in PLY file: %s\n", file); + ply_close(ply); + exit(EXIT_FAILURE); + } + + // Initialize vertex data + data.vertex_count = nvertices; + data.current_vertex = 0; + data.dimension = 3; // Assume 3D points (x, y, z) + data.vertices = calloc2d(nvertices, data.dimension); + + // Set callbacks for vertex coordinates + ply_set_read_cb(ply, "vertex", "x", vertex_cb, &data, 0); + ply_set_read_cb(ply, "vertex", "y", vertex_cb, &data, 1); + ply_set_read_cb(ply, "vertex", "z", vertex_cb, &data, 2); + + // Read PLY data + if (!ply_read(ply)) { + printf("ERROR: Failed to read PLY data from file: %s\n", file); + free2d(data.vertices, nvertices); + ply_close(ply); + exit(EXIT_FAILURE); + } + + // Close PLY file + ply_close(ply); + + // Set output parameters + *nr = nvertices; + *nc = data.dimension; + + return data.vertices; +} \ No newline at end of file diff --git a/base/plyreader.h b/base/plyreader.h new file mode 100644 index 0000000..9dfa301 --- /dev/null +++ b/base/plyreader.h @@ -0,0 +1,27 @@ +// Copyright (c) 2023 +// +// 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 PLYREADER_H +#define PLYREADER_H + +// Function to read PLY file and return vertices as a 2D array +double **read_ply(int *nr, int *nc, const char *file, const char *na); + +#endif // PLYREADER_H \ No newline at end of file diff --git a/base/plywriter.c b/base/plywriter.c new file mode 100644 index 0000000..2dccabf --- /dev/null +++ b/base/plywriter.c @@ -0,0 +1,67 @@ +// Copyright (c) 2023 +// +// 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. + +#include +#include +#include +#include "rply/rply.h" +#include "util.h" +#include "plywriter.h" + +// Function to write a point cloud to a PLY file +int write_ply(double **points, int nr, int nc, const char *filename) { + p_ply ply; + int i, j; + + // Create PLY file + ply = ply_create(filename, PLY_ASCII, NULL, 0, NULL); + if (!ply) { + printf("ERROR: Failed to create PLY file: %s\n", filename); + return 0; + } + + // Add vertex element + ply_add_element(ply, "vertex", nr); + ply_add_property(ply, "x", PLY_FLOAT, PLY_FLOAT, PLY_FLOAT); + ply_add_property(ply, "y", PLY_FLOAT, PLY_FLOAT, PLY_FLOAT); + ply_add_property(ply, "z", PLY_FLOAT, PLY_FLOAT, PLY_FLOAT); + + // Write header + if (!ply_write_header(ply)) { + printf("ERROR: Failed to write PLY header to file: %s\n", filename); + ply_close(ply); + return 0; + } + + // Write vertex data + for (i = 0; i < nr; i++) { + for (j = 0; j < nc; j++) { + ply_write(ply, points[i][j]); + } + } + + // Close PLY file + if (!ply_close(ply)) { + printf("ERROR: Failed to close PLY file: %s\n", filename); + return 0; + } + + return 1; +} \ No newline at end of file diff --git a/base/plywriter.h b/base/plywriter.h new file mode 100644 index 0000000..4034ebc --- /dev/null +++ b/base/plywriter.h @@ -0,0 +1,27 @@ +// Copyright (c) 2023 +// +// 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 PLYWRITER_H +#define PLYWRITER_H + +// Function to write a point cloud to a PLY file +int write_ply(double **points, int nr, int nc, const char *filename); + +#endif // PLYWRITER_H \ No newline at end of file diff --git a/base/rply/rply.c b/base/rply/rply.c new file mode 100644 index 0000000..7c7e72a --- /dev/null +++ b/base/rply/rply.c @@ -0,0 +1,1616 @@ +/* ---------------------------------------------------------------------- + * RPly library, read/write PLY files + * Diego Nehab, IMPA + * http://www.impa.br/~diego/software/rply + * + * This library is distributed under the MIT License. See notice + * at the end of this file. + * ---------------------------------------------------------------------- */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rply.h" +#include "rplyfile.h" + +/* ---------------------------------------------------------------------- + * Make sure we get our integer types right + * ---------------------------------------------------------------------- */ +#if defined(_MSC_VER) && (_MSC_VER < 1600) +/* C99 stdint.h only supported in MSVC++ 10.0 and up */ +typedef __int8 t_ply_int8; +typedef __int16 t_ply_int16; +typedef __int32 t_ply_int32; +typedef unsigned __int8 t_ply_uint8; +typedef unsigned __int16 t_ply_uint16; +typedef unsigned __int32 t_ply_uint32; +#define PLY_INT8_MAX (127) +#define PLY_INT8_MIN (-PLY_INT8_MAX-1) +#define PLY_INT16_MAX (32767) +#define PLY_INT16_MIN (-PLY_INT16_MAX-1) +#define PLY_INT32_MAX (2147483647) +#define PLY_INT32_MIN (-PLY_INT32_MAX-1) +#define PLY_UINT8_MAX (255) +#define PLY_UINT16_MAX (65535) +#define PLY_UINT32_MAX (4294967295) +#else +#include +typedef int8_t t_ply_int8; +typedef int16_t t_ply_int16; +typedef int32_t t_ply_int32; +typedef uint8_t t_ply_uint8; +typedef uint16_t t_ply_uint16; +typedef uint32_t t_ply_uint32; +#define PLY_INT8_MIN INT8_MIN +#define PLY_INT8_MAX INT8_MAX +#define PLY_INT16_MIN INT16_MIN +#define PLY_INT16_MAX INT16_MAX +#define PLY_INT32_MIN INT32_MIN +#define PLY_INT32_MAX INT32_MAX +#define PLY_UINT8_MAX UINT8_MAX +#define PLY_UINT16_MAX UINT16_MAX +#define PLY_UINT32_MAX UINT32_MAX +#endif + +/* ---------------------------------------------------------------------- + * Constants + * ---------------------------------------------------------------------- */ +#define WORDSIZE 256 +#define LINESIZE 1024 +#define BUFFERSIZE (8*1024) + +typedef enum e_ply_io_mode_ { + PLY_READ, + PLY_WRITE +} e_ply_io_mode; + +static const char *const ply_storage_mode_list[] = { + "binary_big_endian", "binary_little_endian", "ascii", NULL +}; /* order matches e_ply_storage_mode enum */ + +static const char *const ply_type_list[] = { + "int8", "uint8", "int16", "uint16", + "int32", "uint32", "float32", "float64", + "char", "uchar", "short", "ushort", + "int", "uint", "float", "double", + "list", NULL +}; /* order matches e_ply_type enum */ + +/* ---------------------------------------------------------------------- + * Property reading callback argument + * + * element: name of element being processed + * property: name of property being processed + * nelements: number of elements of this kind in file + * instance_index: index current element of this kind being processed + * length: number of values in current list (or 1 for scalars) + * value_index: index of current value int this list (or 0 for scalars) + * value: value of property + * pdata/idata: user data defined with ply_set_cb + * + * Returns handle to PLY file if succesful, NULL otherwise. + * ---------------------------------------------------------------------- */ +typedef struct t_ply_argument_ { + p_ply_element element; + long instance_index; + p_ply_property property; + long length, value_index; + double value; + void *pdata; + long idata; +} t_ply_argument; + +/* ---------------------------------------------------------------------- + * Property information + * + * name: name of this property + * type: type of this property (list or type of scalar value) + * length_type, value_type: type of list property count and values + * read_cb: function to be called when this property is called + * + * Returns 1 if should continue processing file, 0 if should abort. + * ---------------------------------------------------------------------- */ +typedef struct t_ply_property_ { + char name[WORDSIZE]; + e_ply_type type, value_type, length_type; + p_ply_read_cb read_cb; + void *pdata; + long idata; +} t_ply_property; + +/* ---------------------------------------------------------------------- + * Element information + * + * name: name of this property + * ninstances: number of elements of this type in file + * property: property descriptions for this element + * nproperty: number of properties in this element + * + * Returns 1 if should continue processing file, 0 if should abort. + * ---------------------------------------------------------------------- */ +typedef struct t_ply_element_ { + char name[WORDSIZE]; + long ninstances; + p_ply_property property; + long nproperties; +} t_ply_element; + +/* ---------------------------------------------------------------------- + * Input/output driver + * + * Depending on file mode, different functions are used to read/write + * property fields. The drivers make it transparent to read/write in ascii, + * big endian or little endian cases. + * ---------------------------------------------------------------------- */ +typedef int (*p_ply_ihandler)(p_ply ply, double *value); +typedef int (*p_ply_ichunk)(p_ply ply, void *anydata, size_t size); +typedef struct t_ply_idriver_ { + p_ply_ihandler ihandler[16]; + p_ply_ichunk ichunk; + const char *name; +} t_ply_idriver; +typedef t_ply_idriver *p_ply_idriver; + +typedef int (*p_ply_ohandler)(p_ply ply, double value); +typedef int (*p_ply_ochunk)(p_ply ply, void *anydata, size_t size); +typedef struct t_ply_odriver_ { + p_ply_ohandler ohandler[16]; + p_ply_ochunk ochunk; + const char *name; +} t_ply_odriver; +typedef t_ply_odriver *p_ply_odriver; + +/* ---------------------------------------------------------------------- + * Ply file handle. + * + * io_mode: read or write (from e_ply_io_mode) + * storage_mode: mode of file associated with handle (from e_ply_storage_mode) + * element: elements description for this file + * nelement: number of different elements in file + * comment: comments for this file + * ncomments: number of comments in file + * obj_info: obj_info items for this file + * nobj_infos: number of obj_info items in file + * fp: file pointer associated with ply file + * rn: skip extra char after end_header? + * buffer: last word/chunck of data read from ply file + * buffer_first, buffer_last: interval of untouched good data in buffer + * buffer_token: start of parsed token (line or word) in buffer + * idriver, odriver: input driver used to get property fields from file + * argument: storage space for callback arguments + * welement, wproperty: element/property type being written + * winstance_index: index of instance of current element being written + * wvalue_index: index of list property value being written + * wlength: number of values in list property being written + * error_cb: error callback + * pdata/idata: user data defined with ply_open/ply_create + * ---------------------------------------------------------------------- */ +typedef struct t_ply_ { + e_ply_io_mode io_mode; + e_ply_storage_mode storage_mode; + p_ply_element element; + long nelements; + char *comment; + long ncomments; + char *obj_info; + long nobj_infos; + FILE *fp; + int own_fp; + int rn; + char buffer[BUFFERSIZE]; + size_t buffer_first, buffer_token, buffer_last; + p_ply_idriver idriver; + p_ply_odriver odriver; + t_ply_argument argument; + long welement, wproperty; + long winstance_index, wvalue_index, wlength; + p_ply_error_cb error_cb; + void *pdata; + long idata; +} t_ply; + +/* ---------------------------------------------------------------------- + * I/O functions and drivers + * ---------------------------------------------------------------------- */ +static t_ply_idriver ply_idriver_ascii; +static t_ply_idriver ply_idriver_binary; +static t_ply_idriver ply_idriver_binary_reverse; +static t_ply_odriver ply_odriver_ascii; +static t_ply_odriver ply_odriver_binary; +static t_ply_odriver ply_odriver_binary_reverse; + +static int ply_read_word(p_ply ply); +static int ply_check_word(p_ply ply); +static void ply_finish_word(p_ply ply, size_t size); +static int ply_read_line(p_ply ply); +static int ply_check_line(p_ply ply); +static int ply_read_chunk(p_ply ply, void *anybuffer, size_t size); +static int ply_read_chunk_reverse(p_ply ply, void *anybuffer, size_t size); +static int ply_write_chunk(p_ply ply, void *anybuffer, size_t size); +static int ply_write_chunk_reverse(p_ply ply, void *anybuffer, size_t size); +static void ply_reverse(void *anydata, size_t size); + +/* ---------------------------------------------------------------------- + * String functions + * ---------------------------------------------------------------------- */ +static int ply_find_string(const char *item, const char* const list[]); +static p_ply_element ply_find_element(p_ply ply, const char *name); +static p_ply_property ply_find_property(p_ply_element element, + const char *name); + +/* ---------------------------------------------------------------------- + * Header parsing + * ---------------------------------------------------------------------- */ +static int ply_read_header_magic(p_ply ply); +static int ply_read_header_format(p_ply ply); +static int ply_read_header_comment(p_ply ply); +static int ply_read_header_obj_info(p_ply ply); +static int ply_read_header_property(p_ply ply); +static int ply_read_header_element(p_ply ply); + +/* ---------------------------------------------------------------------- + * Error handling + * ---------------------------------------------------------------------- */ +static void ply_error_cb(p_ply ply, const char *message); +static void ply_ferror(p_ply ply, const char *fmt, ...); + +/* ---------------------------------------------------------------------- + * Memory allocation and initialization + * ---------------------------------------------------------------------- */ +static void ply_init(p_ply ply); +static void ply_element_init(p_ply_element element); +static void ply_property_init(p_ply_property property); +static p_ply ply_alloc(void); +static p_ply_element ply_grow_element(p_ply ply); +static p_ply_property ply_grow_property(p_ply ply, p_ply_element element); +static void *ply_grow_array(p_ply ply, void **pointer, long *nmemb, long size); + +/* ---------------------------------------------------------------------- + * Special functions + * ---------------------------------------------------------------------- */ +static e_ply_storage_mode ply_arch_endian(void); +static int ply_type_check(void); + +/* ---------------------------------------------------------------------- + * Auxiliary read functions + * ---------------------------------------------------------------------- */ +static int ply_read_element(p_ply ply, p_ply_element element, + p_ply_argument argument); +static int ply_read_property(p_ply ply, p_ply_element element, + p_ply_property property, p_ply_argument argument); +static int ply_read_list_property(p_ply ply, p_ply_element element, + p_ply_property property, p_ply_argument argument); +static int ply_read_scalar_property(p_ply ply, p_ply_element element, + p_ply_property property, p_ply_argument argument); + +/* ---------------------------------------------------------------------- + * Buffer support functions + * ---------------------------------------------------------------------- */ +/* pointers to tokenized word and line in buffer */ +#define BWORD(p) (p->buffer + p->buffer_token) +#define BLINE(p) (p->buffer + p->buffer_token) + +/* pointer to start of untouched bytes in buffer */ +#define BFIRST(p) (p->buffer + p->buffer_first) + +/* number of bytes untouched in buffer */ +#define BSIZE(p) (p->buffer_last - p->buffer_first) + +/* consumes data from buffer */ +#define BSKIP(p, s) (p->buffer_first += s) + +/* refills the buffer */ +static int BREFILL(p_ply ply) { + /* move untouched data to beginning of buffer */ + size_t size = BSIZE(ply); + memmove(ply->buffer, BFIRST(ply), size); + ply->buffer_last = size; + ply->buffer_first = ply->buffer_token = 0; + /* fill remaining with new data */ + size = fread(ply->buffer+size, 1, BUFFERSIZE-size-1, ply->fp); + /* increase size to account for new data */ + ply->buffer_last += size; + /* place sentinel so we can use str* functions with buffer */ + ply->buffer[ply->buffer_last] = '\0'; + /* check if read failed */ + return size > 0; +} + +/* We don't care about end-of-line, generally, because we + * separate words by any white-space character. + * Unfortunately, in binary mode, right after 'end_header', + * we have to know *exactly* how many characters to skip */ +/* We use the end-of-line marker after the 'ply' magic + * number to figure out what to do */ +static int ply_read_header_magic(p_ply ply) { + char *magic = ply->buffer; + if (!BREFILL(ply)) { + ply->error_cb(ply, "Unable to read magic number from file"); + return 0; + } + /* check if it is ply */ + if (magic[0] != 'p' || magic[1] != 'l' || magic[2] != 'y' + || !isspace(magic[3])) { + ply->error_cb(ply, "Wrong magic number. Expected 'ply'"); + return 0; + } + /* figure out if we have to skip the extra character + * after header when we reach the binary part of file */ + ply->rn = magic[3] == '\r' && magic[4] == '\n'; + BSKIP(ply, 3); + return 1; +} + +/* ---------------------------------------------------------------------- + * Exported functions + * ---------------------------------------------------------------------- */ +/* ---------------------------------------------------------------------- + * Read support functions + * ---------------------------------------------------------------------- */ +p_ply ply_open(const char *name, p_ply_error_cb error_cb, + long idata, void *pdata) { + FILE *fp; + p_ply ply; + if (error_cb == NULL) error_cb = ply_error_cb; + assert(name); + fp = fopen(name, "rb"); + if (!fp) { + error_cb(NULL, "Unable to open file"); + return NULL; + } + ply = ply_open_from_file(fp, error_cb, idata, pdata); + if (ply) ply->own_fp = 1; + else fclose(fp); + return ply; +} + +p_ply ply_open_from_file(FILE *fp, p_ply_error_cb error_cb, + long idata, void *pdata) { + p_ply ply = NULL; + if (error_cb == NULL) error_cb = ply_error_cb; + assert(fp); + if (!ply_type_check()) { + error_cb(ply, "Incompatible type system"); + return NULL; + } + ply = ply_alloc(); + if (!ply) { + error_cb(NULL, "Out of memory"); + return NULL; + } + ply->idata = idata; + ply->pdata = pdata; + ply->io_mode = PLY_READ; + ply->error_cb = error_cb; + ply->fp = fp; + ply->own_fp = 0; + return ply; +} + +int ply_read_header(p_ply ply) { + assert(ply && ply->fp && ply->io_mode == PLY_READ); + if (!ply_read_header_magic(ply)) return 0; + if (!ply_read_word(ply)) return 0; + /* parse file format */ + if (!ply_read_header_format(ply)) { + ply_ferror(ply, "Invalid file format"); + return 0; + } + /* parse elements, comments or obj_infos until the end of header */ + while (strcmp(BWORD(ply), "end_header")) { + if (!ply_read_header_comment(ply) && + !ply_read_header_element(ply) && + !ply_read_header_obj_info(ply)) { + ply_ferror(ply, "Unexpected token '%s'", BWORD(ply)); + return 0; + } + } + /* skip extra character? */ + if (ply->rn) { + if (BSIZE(ply) < 1 && !BREFILL(ply)) { + ply_ferror(ply, "Unexpected end of file"); + return 0; + } + BSKIP(ply, 1); + } + return 1; +} + +long ply_set_read_cb(p_ply ply, const char *element_name, + const char* property_name, p_ply_read_cb read_cb, + void *pdata, long idata) { + p_ply_element element = NULL; + p_ply_property property = NULL; + assert(ply && element_name && property_name); + element = ply_find_element(ply, element_name); + if (!element) return 0; + property = ply_find_property(element, property_name); + if (!property) return 0; + property->read_cb = read_cb; + property->pdata = pdata; + property->idata = idata; + return (int) element->ninstances; +} + +int ply_read(p_ply ply) { + long i; + p_ply_argument argument; + assert(ply && ply->fp && ply->io_mode == PLY_READ); + argument = &ply->argument; + /* for each element type */ + for (i = 0; i < ply->nelements; i++) { + p_ply_element element = &ply->element[i]; + argument->element = element; + if (!ply_read_element(ply, element, argument)) + return 0; + } + return 1; +} + +/* ---------------------------------------------------------------------- + * Write support functions + * ---------------------------------------------------------------------- */ +p_ply ply_create(const char *name, e_ply_storage_mode storage_mode, + p_ply_error_cb error_cb, long idata, void *pdata) { + p_ply ply = NULL; + FILE *fp = NULL; + assert(name && storage_mode <= PLY_DEFAULT); + if (error_cb == NULL) error_cb = ply_error_cb; + fp = fopen(name, "wb"); + if (!fp) { + error_cb(ply, "Unable to create file"); + return NULL; + } + ply = ply_create_to_file(fp, storage_mode, error_cb, idata, pdata); + if (ply) ply->own_fp = 1; + else fclose(fp); + return ply; +} + +p_ply ply_create_to_file(FILE *fp, e_ply_storage_mode storage_mode, + p_ply_error_cb error_cb, long idata, void *pdata) { + p_ply ply = NULL; + assert(fp && storage_mode <= PLY_DEFAULT); + if (!ply_type_check()) { + error_cb(ply, "Incompatible type system"); + return NULL; + } + ply = ply_alloc(); + if (!ply) { + error_cb(NULL, "Out of memory"); + return NULL; + } + ply->idata = idata; + ply->pdata = pdata; + ply->io_mode = PLY_WRITE; + if (storage_mode == PLY_DEFAULT) storage_mode = ply_arch_endian(); + if (storage_mode == PLY_ASCII) ply->odriver = &ply_odriver_ascii; + else if (storage_mode == ply_arch_endian()) + ply->odriver = &ply_odriver_binary; + else ply->odriver = &ply_odriver_binary_reverse; + ply->storage_mode = storage_mode; + ply->fp = fp; + ply->own_fp = 0; + ply->error_cb = error_cb; + return ply; +} + + +int ply_add_element(p_ply ply, const char *name, long ninstances) { + p_ply_element element = NULL; + assert(ply && ply->fp && ply->io_mode == PLY_WRITE); + assert(name && strlen(name) < WORDSIZE && ninstances >= 0); + if (strlen(name) >= WORDSIZE || ninstances < 0) { + ply_ferror(ply, "Invalid arguments"); + return 0; + } + element = ply_grow_element(ply); + if (!element) return 0; + strcpy(element->name, name); + element->ninstances = ninstances; + return 1; +} + +int ply_add_scalar_property(p_ply ply, const char *name, e_ply_type type) { + p_ply_element element = NULL; + p_ply_property property = NULL; + assert(ply && ply->fp && ply->io_mode == PLY_WRITE); + assert(name && strlen(name) < WORDSIZE); + assert(type < PLY_LIST); + if (strlen(name) >= WORDSIZE || type >= PLY_LIST) { + ply_ferror(ply, "Invalid arguments"); + return 0; + } + element = &ply->element[ply->nelements-1]; + property = ply_grow_property(ply, element); + if (!property) return 0; + strcpy(property->name, name); + property->type = type; + return 1; +} + +int ply_add_list_property(p_ply ply, const char *name, + e_ply_type length_type, e_ply_type value_type) { + p_ply_element element = NULL; + p_ply_property property = NULL; + assert(ply && ply->fp && ply->io_mode == PLY_WRITE); + assert(name && strlen(name) < WORDSIZE); + if (strlen(name) >= WORDSIZE) { + ply_ferror(ply, "Invalid arguments"); + return 0; + } + assert(length_type < PLY_LIST); + assert(value_type < PLY_LIST); + if (length_type >= PLY_LIST || value_type >= PLY_LIST) { + ply_ferror(ply, "Invalid arguments"); + return 0; + } + element = &ply->element[ply->nelements-1]; + property = ply_grow_property(ply, element); + if (!property) return 0; + strcpy(property->name, name); + property->type = PLY_LIST; + property->length_type = length_type; + property->value_type = value_type; + return 1; +} + +int ply_add_property(p_ply ply, const char *name, e_ply_type type, + e_ply_type length_type, e_ply_type value_type) { + if (type == PLY_LIST) + return ply_add_list_property(ply, name, length_type, value_type); + else + return ply_add_scalar_property(ply, name, type); +} + +int ply_add_comment(p_ply ply, const char *comment) { + char *new_comment = NULL; + assert(ply && comment && strlen(comment) < LINESIZE); + if (!comment || strlen(comment) >= LINESIZE) { + ply_ferror(ply, "Invalid arguments"); + return 0; + } + new_comment = (char *) ply_grow_array(ply, (void **) &ply->comment, + &ply->ncomments, LINESIZE); + if (!new_comment) return 0; + strcpy(new_comment, comment); + return 1; +} + +int ply_add_obj_info(p_ply ply, const char *obj_info) { + char *new_obj_info = NULL; + assert(ply && obj_info && strlen(obj_info) < LINESIZE); + if (!obj_info || strlen(obj_info) >= LINESIZE) { + ply_ferror(ply, "Invalid arguments"); + return 0; + } + new_obj_info = (char *) ply_grow_array(ply, (void **) &ply->obj_info, + &ply->nobj_infos, LINESIZE); + if (!new_obj_info) return 0; + strcpy(new_obj_info, obj_info); + return 1; +} + +int ply_write_header(p_ply ply) { + long i, j; + assert(ply && ply->fp && ply->io_mode == PLY_WRITE); + assert(ply->element || ply->nelements == 0); + assert(!ply->element || ply->nelements > 0); + if (fprintf(ply->fp, "ply\nformat %s 1.0\n", + ply_storage_mode_list[ply->storage_mode]) <= 0) goto error; + for (i = 0; i < ply->ncomments; i++) + if (fprintf(ply->fp, "comment %s\n", ply->comment + LINESIZE*i) <= 0) + goto error; + for (i = 0; i < ply->nobj_infos; i++) + if (fprintf(ply->fp, "obj_info %s\n", ply->obj_info + LINESIZE*i) <= 0) + goto error; + for (i = 0; i < ply->nelements; i++) { + p_ply_element element = &ply->element[i]; + assert(element->property || element->nproperties == 0); + assert(!element->property || element->nproperties > 0); + if (fprintf(ply->fp, "element %s %ld\n", element->name, + element->ninstances) <= 0) goto error; + for (j = 0; j < element->nproperties; j++) { + p_ply_property property = &element->property[j]; + if (property->type == PLY_LIST) { + if (fprintf(ply->fp, "property list %s %s %s\n", + ply_type_list[property->length_type], + ply_type_list[property->value_type], + property->name) <= 0) goto error; + } else { + if (fprintf(ply->fp, "property %s %s\n", + ply_type_list[property->type], + property->name) <= 0) goto error; + } + } + } + return fprintf(ply->fp, "end_header\n") > 0; +error: + ply_ferror(ply, "Error writing to file"); + return 0; +} + +int ply_write(p_ply ply, double value) { + p_ply_element element = NULL; + p_ply_property property = NULL; + int type = -1; + int breakafter = 0; + int spaceafter = 1; + if (ply->welement > ply->nelements) return 0; + element = &ply->element[ply->welement]; + if (ply->wproperty > element->nproperties) return 0; + property = &element->property[ply->wproperty]; + if (property->type == PLY_LIST) { + if (ply->wvalue_index == 0) { + type = property->length_type; + ply->wlength = (long) value; + } else type = property->value_type; + } else { + type = property->type; + ply->wlength = 0; + } + if (!ply->odriver->ohandler[type](ply, value)) { + ply_ferror(ply, "Failed writing %s of %s %d (%s: %s)", + property->name, element->name, + ply->winstance_index, + ply->odriver->name, ply_type_list[type]); + return 0; + } + ply->wvalue_index++; + if (ply->wvalue_index > ply->wlength) { + ply->wvalue_index = 0; + ply->wproperty++; + } + if (ply->wproperty >= element->nproperties) { + ply->wproperty = 0; + ply->winstance_index++; + breakafter = 1; + spaceafter = 0; + } + if (ply->winstance_index >= element->ninstances) { + ply->winstance_index = 0; + do { + ply->welement++; + element = &ply->element[ply->welement]; + } while (ply->welement < ply->nelements && !element->ninstances); + } + if (ply->storage_mode == PLY_ASCII) { + return (!spaceafter || putc(' ', ply->fp) > 0) && + (!breakafter || putc('\n', ply->fp) > 0); + } else { + return 1; + } +} + +int ply_close(p_ply ply) { + long i; + assert(ply && ply->fp); + assert(ply->element || ply->nelements == 0); + assert(!ply->element || ply->nelements > 0); + /* write last chunk to file */ + if (ply->io_mode == PLY_WRITE && + fwrite(ply->buffer, 1, ply->buffer_last, ply->fp) < ply->buffer_last) { + ply_ferror(ply, "Error closing up"); + return 0; + } + if (ply->own_fp) fclose(ply->fp); + /* free all memory used by handle */ + if (ply->element) { + for (i = 0; i < ply->nelements; i++) { + p_ply_element element = &ply->element[i]; + if (element->property) free(element->property); + } + free(ply->element); + } + if (ply->obj_info) free(ply->obj_info); + if (ply->comment) free(ply->comment); + free(ply); + return 1; +} + +/* ---------------------------------------------------------------------- + * Query support functions + * ---------------------------------------------------------------------- */ +p_ply_element ply_get_next_element(p_ply ply, + p_ply_element last) { + assert(ply); + if (!last) return ply->element; + last++; + if (last < ply->element + ply->nelements) return last; + else return NULL; +} + +int ply_get_element_info(p_ply_element element, const char** name, + long *ninstances) { + assert(element); + if (name) *name = element->name; + if (ninstances) *ninstances = (long) element->ninstances; + return 1; +} + +p_ply_property ply_get_next_property(p_ply_element element, + p_ply_property last) { + assert(element); + if (!last) return element->property; + last++; + if (last < element->property + element->nproperties) return last; + else return NULL; +} + +int ply_get_property_info(p_ply_property property, const char** name, + e_ply_type *type, e_ply_type *length_type, e_ply_type *value_type) { + assert(property); + if (name) *name = property->name; + if (type) *type = property->type; + if (length_type) *length_type = property->length_type; + if (value_type) *value_type = property->value_type; + return 1; + +} + +const char *ply_get_next_comment(p_ply ply, const char *last) { + assert(ply); + if (!last) return ply->comment; + last += LINESIZE; + if (last < ply->comment + LINESIZE*ply->ncomments) return last; + else return NULL; +} + +const char *ply_get_next_obj_info(p_ply ply, const char *last) { + assert(ply); + if (!last) return ply->obj_info; + last += LINESIZE; + if (last < ply->obj_info + LINESIZE*ply->nobj_infos) return last; + else return NULL; +} + +/* ---------------------------------------------------------------------- + * Callback argument support functions + * ---------------------------------------------------------------------- */ +int ply_get_argument_element(p_ply_argument argument, + p_ply_element *element, long *instance_index) { + assert(argument); + if (!argument) return 0; + if (element) *element = argument->element; + if (instance_index) *instance_index = argument->instance_index; + return 1; +} + +int ply_get_argument_property(p_ply_argument argument, + p_ply_property *property, long *length, long *value_index) { + assert(argument); + if (!argument) return 0; + if (property) *property = argument->property; + if (length) *length = argument->length; + if (value_index) *value_index = argument->value_index; + return 1; +} + +int ply_get_argument_user_data(p_ply_argument argument, void **pdata, + long *idata) { + assert(argument); + if (!argument) return 0; + if (pdata) *pdata = argument->pdata; + if (idata) *idata = argument->idata; + return 1; +} + +double ply_get_argument_value(p_ply_argument argument) { + assert(argument); + if (!argument) return 0.0; + return argument->value; +} + +int ply_get_ply_user_data(p_ply ply, void **pdata, long *idata) { + assert(ply); + if (!ply) return 0; + if (pdata) *pdata = ply->pdata; + if (idata) *idata = ply->idata; + return 1; +} + +/* ---------------------------------------------------------------------- + * Internal functions + * ---------------------------------------------------------------------- */ +static int ply_read_list_property(p_ply ply, p_ply_element element, + p_ply_property property, p_ply_argument argument) { + int l; + p_ply_read_cb read_cb = property->read_cb; + p_ply_ihandler *driver = ply->idriver->ihandler; + /* get list length */ + p_ply_ihandler handler = driver[property->length_type]; + double length; + if (!handler(ply, &length)) { + ply_ferror(ply, "Error reading '%s' of '%s' number %d", + property->name, element->name, argument->instance_index); + return 0; + } + /* invoke callback to pass length in value field */ + argument->length = (long) length; + argument->value_index = -1; + argument->value = length; + if (read_cb && !read_cb(argument)) { + ply_ferror(ply, "Aborted by user"); + return 0; + } + /* read list values */ + handler = driver[property->value_type]; + /* for each value in list */ + for (l = 0; l < (long) length; l++) { + /* read value from file */ + argument->value_index = l; + if (!handler(ply, &argument->value)) { + ply_ferror(ply, "Error reading value number %d of '%s' of " + "'%s' number %d", l+1, property->name, + element->name, argument->instance_index); + return 0; + } + /* invoke callback to pass value */ + if (read_cb && !read_cb(argument)) { + ply_ferror(ply, "Aborted by user"); + return 0; + } + } + return 1; +} + +static int ply_read_scalar_property(p_ply ply, p_ply_element element, + p_ply_property property, p_ply_argument argument) { + p_ply_read_cb read_cb = property->read_cb; + p_ply_ihandler *driver = ply->idriver->ihandler; + p_ply_ihandler handler = driver[property->type]; + argument->length = 1; + argument->value_index = 0; + if (!handler(ply, &argument->value)) { + ply_ferror(ply, "Error reading '%s' of '%s' number %d", + property->name, element->name, argument->instance_index); + return 0; + } + if (read_cb && !read_cb(argument)) { + ply_ferror(ply, "Aborted by user"); + return 0; + } + return 1; +} + +static int ply_read_property(p_ply ply, p_ply_element element, + p_ply_property property, p_ply_argument argument) { + if (property->type == PLY_LIST) + return ply_read_list_property(ply, element, property, argument); + else + return ply_read_scalar_property(ply, element, property, argument); +} + +static int ply_read_element(p_ply ply, p_ply_element element, + p_ply_argument argument) { + long j, k; + /* for each element of this type */ + for (j = 0; j < element->ninstances; j++) { + argument->instance_index = j; + /* for each property */ + for (k = 0; k < element->nproperties; k++) { + p_ply_property property = &element->property[k]; + argument->property = property; + argument->pdata = property->pdata; + argument->idata = property->idata; + if (!ply_read_property(ply, element, property, argument)) + return 0; + } + } + return 1; +} + +static int ply_find_string(const char *item, const char* const list[]) { + int i; + assert(item && list); + for (i = 0; list[i]; i++) + if (!strcmp(list[i], item)) return i; + return -1; +} + +static p_ply_element ply_find_element(p_ply ply, const char *name) { + p_ply_element element; + int i, nelements; + assert(ply && name); + element = ply->element; + nelements = ply->nelements; + assert(element || nelements == 0); + assert(!element || nelements > 0); + for (i = 0; i < nelements; i++) + if (!strcmp(element[i].name, name)) return &element[i]; + return NULL; +} + +static p_ply_property ply_find_property(p_ply_element element, + const char *name) { + p_ply_property property; + int i, nproperties; + assert(element && name); + property = element->property; + nproperties = element->nproperties; + assert(property || nproperties == 0); + assert(!property || nproperties > 0); + for (i = 0; i < nproperties; i++) + if (!strcmp(property[i].name, name)) return &property[i]; + return NULL; +} + +static int ply_check_word(p_ply ply) { + size_t size = strlen(BWORD(ply)); + if (size >= WORDSIZE) { + ply_ferror(ply, "Word too long"); + return 0; + } else if (size == 0) { + ply_ferror(ply, "Unexpected end of file"); + return 0; + } + return 1; +} + +static int ply_read_word(p_ply ply) { + size_t t = 0; + assert(ply && ply->fp && ply->io_mode == PLY_READ); + /* skip leading blanks */ + while (1) { + t = strspn(BFIRST(ply), " \n\r\t"); + /* check if all buffer was made of blanks */ + if (t >= BSIZE(ply)) { + if (!BREFILL(ply)) { + ply_ferror(ply, "Unexpected end of file"); + return 0; + } + } else break; + } + BSKIP(ply, t); + /* look for a space after the current word */ + t = strcspn(BFIRST(ply), " \n\r\t"); + /* if we didn't reach the end of the buffer, we are done */ + if (t < BSIZE(ply)) { + ply_finish_word(ply, t); + return ply_check_word(ply); + } + /* otherwise, try to refill buffer */ + if (!BREFILL(ply)) { + /* if we reached the end of file, try to do with what we have */ + ply_finish_word(ply, t); + return ply_check_word(ply); + /* ply_ferror(ply, "Unexpected end of file"); */ + /* return 0; */ + } + /* keep looking from where we left */ + t += strcspn(BFIRST(ply) + t, " \n\r\t"); + /* check if the token is too large for our buffer */ + if (t >= BSIZE(ply)) { + ply_ferror(ply, "Token too large"); + return 0; + } + /* we are done */ + ply_finish_word(ply, t); + return ply_check_word(ply); +} + +static void ply_finish_word(p_ply ply, size_t size) { + ply->buffer_token = ply->buffer_first; + BSKIP(ply, size); + *BFIRST(ply) = '\0'; + BSKIP(ply, 1); +} + +static int ply_check_line(p_ply ply) { + if (strlen(BLINE(ply)) >= LINESIZE) { + ply_ferror(ply, "Line too long"); + return 0; + } + return 1; +} + +static int ply_read_line(p_ply ply) { + const char *end = NULL; + assert(ply && ply->fp && ply->io_mode == PLY_READ); + /* look for a end of line */ + end = strchr(BFIRST(ply), '\n'); + /* if we didn't reach the end of the buffer, we are done */ + if (end) { + ply->buffer_token = ply->buffer_first; + BSKIP(ply, end - BFIRST(ply)); + *BFIRST(ply) = '\0'; + BSKIP(ply, 1); + return ply_check_line(ply); + } else { + end = ply->buffer + BSIZE(ply); + /* otherwise, try to refill buffer */ + if (!BREFILL(ply)) { + ply_ferror(ply, "Unexpected end of file"); + return 0; + } + } + /* keep looking from where we left */ + end = strchr(end, '\n'); + /* check if the token is too large for our buffer */ + if (!end) { + ply_ferror(ply, "Token too large"); + return 0; + } + /* we are done */ + ply->buffer_token = ply->buffer_first; + BSKIP(ply, end - BFIRST(ply)); + *BFIRST(ply) = '\0'; + BSKIP(ply, 1); + return ply_check_line(ply); +} + +static int ply_read_chunk(p_ply ply, void *anybuffer, size_t size) { + char *buffer = (char *) anybuffer; + size_t i = 0; + assert(ply && ply->fp && ply->io_mode == PLY_READ); + assert(ply->buffer_first <= ply->buffer_last); + while (i < size) { + if (ply->buffer_first < ply->buffer_last) { + buffer[i] = ply->buffer[ply->buffer_first]; + ply->buffer_first++; + i++; + } else { + ply->buffer_first = 0; + ply->buffer_last = fread(ply->buffer, 1, BUFFERSIZE, ply->fp); + if (ply->buffer_last <= 0) return 0; + } + } + return 1; +} + +static int ply_write_chunk(p_ply ply, void *anybuffer, size_t size) { + char *buffer = (char *) anybuffer; + size_t i = 0; + assert(ply && ply->fp && ply->io_mode == PLY_WRITE); + assert(ply->buffer_last <= BUFFERSIZE); + while (i < size) { + if (ply->buffer_last < BUFFERSIZE) { + ply->buffer[ply->buffer_last] = buffer[i]; + ply->buffer_last++; + i++; + } else { + ply->buffer_last = 0; + if (fwrite(ply->buffer, 1, BUFFERSIZE, ply->fp) < BUFFERSIZE) + return 0; + } + } + return 1; +} + +static int ply_write_chunk_reverse(p_ply ply, void *anybuffer, size_t size) { + int ret = 0; + ply_reverse(anybuffer, size); + ret = ply_write_chunk(ply, anybuffer, size); + ply_reverse(anybuffer, size); + return ret; +} + +static int ply_read_chunk_reverse(p_ply ply, void *anybuffer, size_t size) { + if (!ply_read_chunk(ply, anybuffer, size)) return 0; + ply_reverse(anybuffer, size); + return 1; +} + +static void ply_reverse(void *anydata, size_t size) { + char *data = (char *) anydata; + char temp; + size_t i; + for (i = 0; i < size/2; i++) { + temp = data[i]; + data[i] = data[size-i-1]; + data[size-i-1] = temp; + } +} + +static void ply_init(p_ply ply) { + ply->element = NULL; + ply->nelements = 0; + ply->comment = NULL; + ply->ncomments = 0; + ply->obj_info = NULL; + ply->nobj_infos = 0; + ply->idriver = NULL; + ply->odriver = NULL; + ply->buffer[0] = '\0'; + ply->buffer_first = ply->buffer_last = ply->buffer_token = 0; + ply->welement = 0; + ply->wproperty = 0; + ply->winstance_index = 0; + ply->wlength = 0; + ply->wvalue_index = 0; +} + +static void ply_element_init(p_ply_element element) { + element->name[0] = '\0'; + element->ninstances = 0; + element->property = NULL; + element->nproperties = 0; +} + +static void ply_property_init(p_ply_property property) { + property->name[0] = '\0'; + property->type = -1; + property->length_type = -1; + property->value_type = -1; + property->read_cb = (p_ply_read_cb) NULL; + property->pdata = NULL; + property->idata = 0; +} + +static p_ply ply_alloc(void) { + p_ply ply = (p_ply) calloc(1, sizeof(t_ply)); + if (!ply) return NULL; + ply_init(ply); + return ply; +} + +static void *ply_grow_array(p_ply ply, void **pointer, + long *nmemb, long size) { + void *temp = *pointer; + long count = *nmemb + 1; + if (!temp) temp = malloc(count*size); + else temp = realloc(temp, count*size); + if (!temp) { + ply_ferror(ply, "Out of memory"); + return NULL; + } + *pointer = temp; + *nmemb = count; + return (char *) temp + (count-1) * size; +} + +static p_ply_element ply_grow_element(p_ply ply) { + p_ply_element element = NULL; + assert(ply); + assert(ply->element || ply->nelements == 0); + assert(!ply->element || ply->nelements > 0); + element = (p_ply_element) ply_grow_array(ply, (void **) &ply->element, + &ply->nelements, sizeof(t_ply_element)); + if (!element) return NULL; + ply_element_init(element); + return element; +} + +static p_ply_property ply_grow_property(p_ply ply, p_ply_element element) { + p_ply_property property = NULL; + assert(ply); + assert(element); + assert(element->property || element->nproperties == 0); + assert(!element->property || element->nproperties > 0); + property = (p_ply_property) ply_grow_array(ply, + (void **) &element->property, + &element->nproperties, sizeof(t_ply_property)); + if (!property) return NULL; + ply_property_init(property); + return property; +} + +static int ply_read_header_format(p_ply ply) { + assert(ply && ply->fp && ply->io_mode == PLY_READ); + if (strcmp(BWORD(ply), "format")) return 0; + if (!ply_read_word(ply)) return 0; + ply->storage_mode = ply_find_string(BWORD(ply), ply_storage_mode_list); + if (ply->storage_mode == (e_ply_storage_mode) (-1)) return 0; + if (ply->storage_mode == PLY_ASCII) ply->idriver = &ply_idriver_ascii; + else if (ply->storage_mode == ply_arch_endian()) + ply->idriver = &ply_idriver_binary; + else ply->idriver = &ply_idriver_binary_reverse; + if (!ply_read_word(ply)) return 0; + if (strcmp(BWORD(ply), "1.0")) return 0; + if (!ply_read_word(ply)) return 0; + return 1; +} + +static int ply_read_header_comment(p_ply ply) { + assert(ply && ply->fp && ply->io_mode == PLY_READ); + if (strcmp(BWORD(ply), "comment")) return 0; + if (!ply_read_line(ply)) return 0; + if (!ply_add_comment(ply, BLINE(ply))) return 0; + if (!ply_read_word(ply)) return 0; + return 1; +} + +static int ply_read_header_obj_info(p_ply ply) { + assert(ply && ply->fp && ply->io_mode == PLY_READ); + if (strcmp(BWORD(ply), "obj_info")) return 0; + if (!ply_read_line(ply)) return 0; + if (!ply_add_obj_info(ply, BLINE(ply))) return 0; + if (!ply_read_word(ply)) return 0; + return 1; +} + +static int ply_read_header_property(p_ply ply) { + p_ply_element element = NULL; + p_ply_property property = NULL; + /* make sure it is a property */ + if (strcmp(BWORD(ply), "property")) return 0; + element = &ply->element[ply->nelements-1]; + property = ply_grow_property(ply, element); + if (!property) return 0; + /* get property type */ + if (!ply_read_word(ply)) return 0; + property->type = ply_find_string(BWORD(ply), ply_type_list); + if (property->type == (e_ply_type) (-1)) return 0; + if (property->type == PLY_LIST) { + /* if it's a list, we need the base types */ + if (!ply_read_word(ply)) return 0; + property->length_type = ply_find_string(BWORD(ply), ply_type_list); + if (property->length_type == (e_ply_type) (-1)) return 0; + if (!ply_read_word(ply)) return 0; + property->value_type = ply_find_string(BWORD(ply), ply_type_list); + if (property->value_type == (e_ply_type) (-1)) return 0; + } + /* get property name */ + if (!ply_read_word(ply)) return 0; + strcpy(property->name, BWORD(ply)); + if (!ply_read_word(ply)) return 0; + return 1; +} + +static int ply_read_header_element(p_ply ply) { + p_ply_element element = NULL; + long dummy; + assert(ply && ply->fp && ply->io_mode == PLY_READ); + if (strcmp(BWORD(ply), "element")) return 0; + /* allocate room for new element */ + element = ply_grow_element(ply); + if (!element) return 0; + /* get element name */ + if (!ply_read_word(ply)) return 0; + strcpy(element->name, BWORD(ply)); + /* get number of elements of this type */ + if (!ply_read_word(ply)) return 0; + if (sscanf(BWORD(ply), "%ld", &dummy) != 1) { + ply_ferror(ply, "Expected number got '%s'", BWORD(ply)); + return 0; + } + element->ninstances = dummy; + /* get all properties for this element */ + if (!ply_read_word(ply)) return 0; + while (ply_read_header_property(ply) || + ply_read_header_comment(ply) || ply_read_header_obj_info(ply)) + /* do nothing */; + return 1; +} + +static void ply_error_cb(p_ply ply, const char *message) { + (void) ply; + fprintf(stderr, "RPly: %s\n", message); +} + +static void ply_ferror(p_ply ply, const char *fmt, ...) { + char buffer[1024]; + va_list ap; + va_start(ap, fmt); + vsprintf(buffer, fmt, ap); + va_end(ap); + ply->error_cb(ply, buffer); +} + +static e_ply_storage_mode ply_arch_endian(void) { + unsigned long i = 1; + unsigned char *s = (unsigned char *) &i; + if (*s == 1) return PLY_LITTLE_ENDIAN; + else return PLY_BIG_ENDIAN; +} + +static int ply_type_check(void) { + assert(sizeof(t_ply_int8) == 1); + assert(sizeof(t_ply_uint8) == 1); + assert(sizeof(t_ply_int16) == 2); + assert(sizeof(t_ply_uint16) == 2); + assert(sizeof(t_ply_int32) == 4); + assert(sizeof(t_ply_uint32) == 4); + assert(sizeof(float) == 4); + assert(sizeof(double) == 8); + if (sizeof(t_ply_int8) != 1) return 0; + if (sizeof(t_ply_uint8) != 1) return 0; + if (sizeof(t_ply_int16) != 2) return 0; + if (sizeof(t_ply_uint16) != 2) return 0; + if (sizeof(t_ply_int32) != 4) return 0; + if (sizeof(t_ply_uint32) != 4) return 0; + if (sizeof(float) != 4) return 0; + if (sizeof(double) != 8) return 0; + return 1; +} + +/* ---------------------------------------------------------------------- + * Output handlers + * ---------------------------------------------------------------------- */ +static int oascii_int8(p_ply ply, double value) { + if (value > PLY_INT8_MAX || value < PLY_INT8_MIN) return 0; + return fprintf(ply->fp, "%d", (t_ply_int8) value) > 0; +} + +static int oascii_uint8(p_ply ply, double value) { + if (value > PLY_UINT8_MAX || value < 0) return 0; + return fprintf(ply->fp, "%d", (t_ply_uint8) value) > 0; +} + +static int oascii_int16(p_ply ply, double value) { + if (value > PLY_INT16_MAX || value < PLY_INT16_MIN) return 0; + return fprintf(ply->fp, "%d", (t_ply_int16) value) > 0; +} + +static int oascii_uint16(p_ply ply, double value) { + if (value > PLY_UINT16_MAX || value < 0) return 0; + return fprintf(ply->fp, "%d", (t_ply_uint16) value) > 0; +} + +static int oascii_int32(p_ply ply, double value) { + if (value > PLY_INT32_MAX || value < PLY_INT32_MIN) return 0; + return fprintf(ply->fp, "%d", (t_ply_int32) value) > 0; +} + +static int oascii_uint32(p_ply ply, double value) { + if (value > PLY_UINT32_MAX || value < 0) return 0; + return fprintf(ply->fp, "%d", (t_ply_uint32) value) > 0; +} + +static int oascii_float32(p_ply ply, double value) { + if (value < -FLT_MAX || value > FLT_MAX) return 0; + return fprintf(ply->fp, "%g", (float) value) > 0; +} + +static int oascii_float64(p_ply ply, double value) { + if (value < -DBL_MAX || value > DBL_MAX) return 0; + return fprintf(ply->fp, "%g", value) > 0; +} + +static int obinary_int8(p_ply ply, double value) { + t_ply_int8 int8 = (t_ply_int8) value; + if (value > PLY_INT8_MAX || value < PLY_INT8_MIN) return 0; + return ply->odriver->ochunk(ply, &int8, sizeof(int8)); +} + +static int obinary_uint8(p_ply ply, double value) { + t_ply_uint8 uint8 = (t_ply_uint8) value; + if (value > PLY_UINT8_MAX || value < 0) return 0; + return ply->odriver->ochunk(ply, &uint8, sizeof(uint8)); +} + +static int obinary_int16(p_ply ply, double value) { + t_ply_int16 int16 = (t_ply_int16) value; + if (value > PLY_INT16_MAX || value < PLY_INT16_MIN) return 0; + return ply->odriver->ochunk(ply, &int16, sizeof(int16)); +} + +static int obinary_uint16(p_ply ply, double value) { + t_ply_uint16 uint16 = (t_ply_uint16) value; + if (value > PLY_UINT16_MAX || value < 0) return 0; + return ply->odriver->ochunk(ply, &uint16, sizeof(uint16)); +} + +static int obinary_int32(p_ply ply, double value) { + t_ply_int32 int32 = (t_ply_int32) value; + if (value > PLY_INT32_MAX || value < PLY_INT32_MIN) return 0; + return ply->odriver->ochunk(ply, &int32, sizeof(int32)); +} + +static int obinary_uint32(p_ply ply, double value) { + t_ply_uint32 uint32 = (t_ply_uint32) value; + if (value > PLY_UINT32_MAX || value < 0) return 0; + return ply->odriver->ochunk(ply, &uint32, sizeof(uint32)); +} + +static int obinary_float32(p_ply ply, double value) { + float float32 = (float) value; + if (value > FLT_MAX || value < -FLT_MAX) return 0; + return ply->odriver->ochunk(ply, &float32, sizeof(float32)); +} + +static int obinary_float64(p_ply ply, double value) { + return ply->odriver->ochunk(ply, &value, sizeof(value)); +} + +/* ---------------------------------------------------------------------- + * Input handlers + * ---------------------------------------------------------------------- */ +static int iascii_int8(p_ply ply, double *value) { + char *end; + if (!ply_read_word(ply)) return 0; + *value = strtol(BWORD(ply), &end, 10); + if (*end || *value > PLY_INT8_MAX || *value < PLY_INT8_MIN) return 0; + return 1; +} + +static int iascii_uint8(p_ply ply, double *value) { + char *end; + if (!ply_read_word(ply)) return 0; + *value = strtol(BWORD(ply), &end, 10); + if (*end || *value > PLY_UINT8_MAX || *value < 0) return 0; + return 1; +} + +static int iascii_int16(p_ply ply, double *value) { + char *end; + if (!ply_read_word(ply)) return 0; + *value = strtol(BWORD(ply), &end, 10); + if (*end || *value > PLY_INT16_MAX || *value < PLY_INT16_MIN) return 0; + return 1; +} + +static int iascii_uint16(p_ply ply, double *value) { + char *end; + if (!ply_read_word(ply)) return 0; + *value = strtol(BWORD(ply), &end, 10); + if (*end || *value > PLY_UINT16_MAX || *value < 0) return 0; + return 1; +} + +static int iascii_int32(p_ply ply, double *value) { + char *end; + if (!ply_read_word(ply)) return 0; + *value = strtol(BWORD(ply), &end, 10); + if (*end || *value > PLY_INT32_MAX || *value < PLY_INT32_MIN) return 0; + return 1; +} + +static int iascii_uint32(p_ply ply, double *value) { + char *end; + if (!ply_read_word(ply)) return 0; + *value = strtol(BWORD(ply), &end, 10); + if (*end || *value > PLY_UINT32_MAX || *value < 0) return 0; + return 1; +} + +static int iascii_float32(p_ply ply, double *value) { + char *end; + if (!ply_read_word(ply)) return 0; + *value = strtod(BWORD(ply), &end); + if (*end || *value < -FLT_MAX || *value > FLT_MAX) return 0; + return 1; +} + +static int iascii_float64(p_ply ply, double *value) { + char *end; + if (!ply_read_word(ply)) return 0; + *value = strtod(BWORD(ply), &end); + if (*end || *value < -DBL_MAX || *value > DBL_MAX) return 0; + return 1; +} + +static int ibinary_int8(p_ply ply, double *value) { + t_ply_int8 int8; + if (!ply->idriver->ichunk(ply, &int8, 1)) return 0; + *value = int8; + return 1; +} + +static int ibinary_uint8(p_ply ply, double *value) { + t_ply_uint8 uint8; + if (!ply->idriver->ichunk(ply, &uint8, 1)) return 0; + *value = uint8; + return 1; +} + +static int ibinary_int16(p_ply ply, double *value) { + t_ply_int16 int16; + if (!ply->idriver->ichunk(ply, &int16, sizeof(int16))) return 0; + *value = int16; + return 1; +} + +static int ibinary_uint16(p_ply ply, double *value) { + t_ply_uint16 uint16; + if (!ply->idriver->ichunk(ply, &uint16, sizeof(uint16))) return 0; + *value = uint16; + return 1; +} + +static int ibinary_int32(p_ply ply, double *value) { + t_ply_int32 int32; + if (!ply->idriver->ichunk(ply, &int32, sizeof(int32))) return 0; + *value = int32; + return 1; +} + +static int ibinary_uint32(p_ply ply, double *value) { + t_ply_uint32 uint32; + if (!ply->idriver->ichunk(ply, &uint32, sizeof(uint32))) return 0; + *value = uint32; + return 1; +} + +static int ibinary_float32(p_ply ply, double *value) { + float float32; + if (!ply->idriver->ichunk(ply, &float32, sizeof(float32))) return 0; + *value = float32; + return 1; +} + +static int ibinary_float64(p_ply ply, double *value) { + return ply->idriver->ichunk(ply, value, sizeof(double)); +} + +/* ---------------------------------------------------------------------- + * Constants + * ---------------------------------------------------------------------- */ +static t_ply_idriver ply_idriver_ascii = { + { iascii_int8, iascii_uint8, iascii_int16, iascii_uint16, + iascii_int32, iascii_uint32, iascii_float32, iascii_float64, + iascii_int8, iascii_uint8, iascii_int16, iascii_uint16, + iascii_int32, iascii_uint32, iascii_float32, iascii_float64 + }, /* order matches e_ply_type enum */ + NULL, + "ascii input" +}; + +static t_ply_idriver ply_idriver_binary = { + { ibinary_int8, ibinary_uint8, ibinary_int16, ibinary_uint16, + ibinary_int32, ibinary_uint32, ibinary_float32, ibinary_float64, + ibinary_int8, ibinary_uint8, ibinary_int16, ibinary_uint16, + ibinary_int32, ibinary_uint32, ibinary_float32, ibinary_float64 + }, /* order matches e_ply_type enum */ + ply_read_chunk, + "binary input" +}; + +static t_ply_idriver ply_idriver_binary_reverse = { + { ibinary_int8, ibinary_uint8, ibinary_int16, ibinary_uint16, + ibinary_int32, ibinary_uint32, ibinary_float32, ibinary_float64, + ibinary_int8, ibinary_uint8, ibinary_int16, ibinary_uint16, + ibinary_int32, ibinary_uint32, ibinary_float32, ibinary_float64 + }, /* order matches e_ply_type enum */ + ply_read_chunk_reverse, + "reverse binary input" +}; + +static t_ply_odriver ply_odriver_ascii = { + { oascii_int8, oascii_uint8, oascii_int16, oascii_uint16, + oascii_int32, oascii_uint32, oascii_float32, oascii_float64, + oascii_int8, oascii_uint8, oascii_int16, oascii_uint16, + oascii_int32, oascii_uint32, oascii_float32, oascii_float64 + }, /* order matches e_ply_type enum */ + NULL, + "ascii output" +}; + +static t_ply_odriver ply_odriver_binary = { + { obinary_int8, obinary_uint8, obinary_int16, obinary_uint16, + obinary_int32, obinary_uint32, obinary_float32, obinary_float64, + obinary_int8, obinary_uint8, obinary_int16, obinary_uint16, + obinary_int32, obinary_uint32, obinary_float32, obinary_float64 + }, /* order matches e_ply_type enum */ + ply_write_chunk, + "binary output" +}; + +static t_ply_odriver ply_odriver_binary_reverse = { + { obinary_int8, obinary_uint8, obinary_int16, obinary_uint16, + obinary_int32, obinary_uint32, obinary_float32, obinary_float64, + obinary_int8, obinary_uint8, obinary_int16, obinary_uint16, + obinary_int32, obinary_uint32, obinary_float32, obinary_float64 + }, /* order matches e_ply_type enum */ + ply_write_chunk_reverse, + "reverse binary output" +}; + +/* ---------------------------------------------------------------------- + * Copyright (C) 2003-2015 Diego Nehab. All rights reserved. + * + * 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. + * ---------------------------------------------------------------------- */ diff --git a/base/rply/rply.h b/base/rply/rply.h new file mode 100644 index 0000000..9fa6da9 --- /dev/null +++ b/base/rply/rply.h @@ -0,0 +1,378 @@ +#ifndef RPLY_H +#define RPLY_H +/* ---------------------------------------------------------------------- + * RPly library, read/write PLY files + * Diego Nehab, IMPA + * http://www.impa.br/~diego/software/rply + * + * This library is distributed under the MIT License. See notice + * at the end of this file. + * ---------------------------------------------------------------------- */ + +#ifdef __cplusplus +extern "C" { +#endif + +#define RPLY_VERSION "RPly 1.1.4" +#define RPLY_COPYRIGHT "Copyright (C) 2003-2015 Diego Nehab" +#define RPLY_AUTHORS "Diego Nehab" + +/* ---------------------------------------------------------------------- + * Types + * ---------------------------------------------------------------------- */ +/* structures are opaque */ +typedef struct t_ply_ *p_ply; +typedef struct t_ply_element_ *p_ply_element; +typedef struct t_ply_property_ *p_ply_property; +typedef struct t_ply_argument_ *p_ply_argument; + +/* ply format mode type */ +typedef enum e_ply_storage_mode_ { + PLY_BIG_ENDIAN, + PLY_LITTLE_ENDIAN, + PLY_ASCII, + PLY_DEFAULT /* has to be the last in enum */ +} e_ply_storage_mode; /* order matches ply_storage_mode_list */ + +/* ply data type */ +typedef enum e_ply_type { + PLY_INT8, PLY_UINT8, PLY_INT16, PLY_UINT16, + PLY_INT32, PLY_UIN32, PLY_FLOAT32, PLY_FLOAT64, + PLY_CHAR, PLY_UCHAR, PLY_SHORT, PLY_USHORT, + PLY_INT, PLY_UINT, PLY_FLOAT, PLY_DOUBLE, + PLY_LIST /* has to be the last in enum */ +} e_ply_type; /* order matches ply_type_list */ + +/* ---------------------------------------------------------------------- + * Error callback prototype + * + * message: error message + * ply: handle returned by ply_open or ply_create + * ---------------------------------------------------------------------- */ +typedef void (*p_ply_error_cb)(p_ply ply, const char *message); + +/* ---------------------------------------------------------------------- + * Gets user data from within an error callback + * + * ply: handle returned by ply_open or ply_create + * idata,pdata: contextual information set in ply_open or ply_create + * ---------------------------------------------------------------------- */ +int ply_get_ply_user_data(p_ply ply, void **pdata, long *idata); + +/* ---------------------------------------------------------------------- + * Opens a PLY file for reading (fails if file is not a PLY file) + * + * name: file name + * error_cb: error callback function + * idata,pdata: contextual information available to users + * + * Returns 1 if successful, 0 otherwise + * ---------------------------------------------------------------------- */ +p_ply ply_open(const char *name, p_ply_error_cb error_cb, long idata, + void *pdata); + +/* ---------------------------------------------------------------------- + * Reads and parses the header of a PLY file returned by ply_open + * + * ply: handle returned by ply_open + * + * Returns 1 if successfull, 0 otherwise + * ---------------------------------------------------------------------- */ +int ply_read_header(p_ply ply); + +/* ---------------------------------------------------------------------- + * Property reading callback prototype + * + * argument: parameters for property being processed when callback is called + * + * Returns 1 if should continue processing file, 0 if should abort. + * ---------------------------------------------------------------------- */ +typedef int (*p_ply_read_cb)(p_ply_argument argument); + +/* ---------------------------------------------------------------------- + * Sets up callbacks for property reading after header was parsed + * + * ply: handle returned by ply_open + * element_name: element where property is + * property_name: property to associate element with + * read_cb: function to be called for each property value + * pdata/idata: user data that will be passed to callback + * + * Returns 0 if no element or no property in element, returns the + * number of element instances otherwise. + * ---------------------------------------------------------------------- */ +long ply_set_read_cb(p_ply ply, const char *element_name, + const char *property_name, p_ply_read_cb read_cb, + void *pdata, long idata); + +/* ---------------------------------------------------------------------- + * Returns information about the element originating a callback + * + * argument: handle to argument + * element: receives a the element handle (if non-null) + * instance_index: receives the index of the current element instance + * (if non-null) + * + * Returns 1 if successfull, 0 otherwise + * ---------------------------------------------------------------------- */ +int ply_get_argument_element(p_ply_argument argument, + p_ply_element *element, long *instance_index); + +/* ---------------------------------------------------------------------- + * Returns information about the property originating a callback + * + * argument: handle to argument + * property: receives the property handle (if non-null) + * length: receives the number of values in this property (if non-null) + * value_index: receives the index of current property value (if non-null) + * + * Returns 1 if successfull, 0 otherwise + * ---------------------------------------------------------------------- */ +int ply_get_argument_property(p_ply_argument argument, + p_ply_property *property, long *length, long *value_index); + +/* ---------------------------------------------------------------------- + * Returns user data associated with callback + * + * pdata: receives a copy of user custom data pointer (if non-null) + * idata: receives a copy of user custom data integer (if non-null) + * + * Returns 1 if successfull, 0 otherwise + * ---------------------------------------------------------------------- */ +int ply_get_argument_user_data(p_ply_argument argument, void **pdata, + long *idata); + +/* ---------------------------------------------------------------------- + * Returns the value associated with a callback + * + * argument: handle to argument + * + * Returns the current data item + * ---------------------------------------------------------------------- */ +double ply_get_argument_value(p_ply_argument argument); + +/* ---------------------------------------------------------------------- + * Reads all elements and properties calling the callbacks defined with + * calls to ply_set_read_cb + * + * ply: handle returned by ply_open + * + * Returns 1 if successfull, 0 otherwise + * ---------------------------------------------------------------------- */ +int ply_read(p_ply ply); + +/* ---------------------------------------------------------------------- + * Iterates over all elements by returning the next element. + * Call with NULL to return handle to first element. + * + * ply: handle returned by ply_open + * last: handle of last element returned (NULL for first element) + * + * Returns element if successfull or NULL if no more elements + * ---------------------------------------------------------------------- */ +p_ply_element ply_get_next_element(p_ply ply, p_ply_element last); + +/* ---------------------------------------------------------------------- + * Iterates over all comments by returning the next comment. + * Call with NULL to return pointer to first comment. + * + * ply: handle returned by ply_open + * last: pointer to last comment returned (NULL for first comment) + * + * Returns comment if successfull or NULL if no more comments + * ---------------------------------------------------------------------- */ +const char *ply_get_next_comment(p_ply ply, const char *last); + +/* ---------------------------------------------------------------------- + * Iterates over all obj_infos by returning the next obj_info. + * Call with NULL to return pointer to first obj_info. + * + * ply: handle returned by ply_open + * last: pointer to last obj_info returned (NULL for first obj_info) + * + * Returns obj_info if successfull or NULL if no more obj_infos + * ---------------------------------------------------------------------- */ +const char *ply_get_next_obj_info(p_ply ply, const char *last); + +/* ---------------------------------------------------------------------- + * Returns information about an element + * + * element: element of interest + * name: receives a pointer to internal copy of element name (if non-null) + * ninstances: receives the number of instances of this element (if non-null) + * + * Returns 1 if successfull or 0 otherwise + * ---------------------------------------------------------------------- */ +int ply_get_element_info(p_ply_element element, const char** name, + long *ninstances); + +/* ---------------------------------------------------------------------- + * Iterates over all properties by returning the next property. + * Call with NULL to return handle to first property. + * + * element: handle of element with the properties of interest + * last: handle of last property returned (NULL for first property) + * + * Returns element if successfull or NULL if no more properties + * ---------------------------------------------------------------------- */ +p_ply_property ply_get_next_property(p_ply_element element, + p_ply_property last); + +/* ---------------------------------------------------------------------- + * Returns information about a property + * + * property: handle to property of interest + * name: receives a pointer to internal copy of property name (if non-null) + * type: receives the property type (if non-null) + * length_type: for list properties, receives the scalar type of + * the length field (if non-null) + * value_type: for list properties, receives the scalar type of the value + * fields (if non-null) + * + * Returns 1 if successfull or 0 otherwise + * ---------------------------------------------------------------------- */ +int ply_get_property_info(p_ply_property property, const char** name, + e_ply_type *type, e_ply_type *length_type, e_ply_type *value_type); + +/* ---------------------------------------------------------------------- + * Creates new PLY file + * + * name: file name + * storage_mode: file format mode + * error_cb: error callback function + * idata,pdata: contextual information available to users + * + * Returns handle to PLY file if successfull, NULL otherwise + * ---------------------------------------------------------------------- */ +p_ply ply_create(const char *name, e_ply_storage_mode storage_mode, + p_ply_error_cb error_cb, long idata, void *pdata); + +/* ---------------------------------------------------------------------- + * Adds a new element to the PLY file created by ply_create + * + * ply: handle returned by ply_create + * name: name of new element + * ninstances: number of element of this time in file + * + * Returns 1 if successfull, 0 otherwise + * ---------------------------------------------------------------------- */ +int ply_add_element(p_ply ply, const char *name, long ninstances); + +/* ---------------------------------------------------------------------- + * Adds a new property to the last element added by ply_add_element + * + * ply: handle returned by ply_create + * name: name of new property + * type: property type + * length_type: scalar type of length field of a list property + * value_type: scalar type of value fields of a list property + * + * Returns 1 if successfull, 0 otherwise + * ---------------------------------------------------------------------- */ +int ply_add_property(p_ply ply, const char *name, e_ply_type type, + e_ply_type length_type, e_ply_type value_type); + +/* ---------------------------------------------------------------------- + * Adds a new list property to the last element added by ply_add_element + * + * ply: handle returned by ply_create + * name: name of new property + * length_type: scalar type of length field of a list property + * value_type: scalar type of value fields of a list property + * + * Returns 1 if successfull, 0 otherwise + * ---------------------------------------------------------------------- */ +int ply_add_list_property(p_ply ply, const char *name, + e_ply_type length_type, e_ply_type value_type); + +/* ---------------------------------------------------------------------- + * Adds a new property to the last element added by ply_add_element + * + * ply: handle returned by ply_create + * name: name of new property + * type: property type + * + * Returns 1 if successfull, 0 otherwise + * ---------------------------------------------------------------------- */ +int ply_add_scalar_property(p_ply ply, const char *name, e_ply_type type); + +/* ---------------------------------------------------------------------- + * Adds a new comment item + * + * ply: handle returned by ply_create + * comment: pointer to string with comment text + * + * Returns 1 if successfull, 0 otherwise + * ---------------------------------------------------------------------- */ +int ply_add_comment(p_ply ply, const char *comment); + +/* ---------------------------------------------------------------------- + * Adds a new obj_info item + * + * ply: handle returned by ply_create + * comment: pointer to string with obj_info data + * + * Returns 1 if successfull, 0 otherwise + * ---------------------------------------------------------------------- */ +int ply_add_obj_info(p_ply ply, const char *obj_info); + +/* ---------------------------------------------------------------------- + * Writes the PLY file header after all element and properties have been + * defined by calls to ply_add_element and ply_add_property + * + * ply: handle returned by ply_create + * + * Returns 1 if successfull, 0 otherwise + * ---------------------------------------------------------------------- */ +int ply_write_header(p_ply ply); + +/* ---------------------------------------------------------------------- + * Writes one property value, in the order they should be written to the + * file. For each element type, write all elements of that type in order. + * For each element, write all its properties in order. For scalar + * properties, just write the value. For list properties, write the length + * and then each of the values. + * + * ply: handle returned by ply_create + * + * Returns 1 if successfull, 0 otherwise + * ---------------------------------------------------------------------- */ +int ply_write(p_ply ply, double value); + +/* ---------------------------------------------------------------------- + * Closes a PLY file handle. Releases all memory used by handle + * + * ply: handle to be closed. + * + * Returns 1 if successfull, 0 otherwise + * ---------------------------------------------------------------------- */ +int ply_close(p_ply ply); + +#ifdef __cplusplus +} +#endif + +#endif /* RPLY_H */ + +/* ---------------------------------------------------------------------- + * Copyright (C) 2003-2015 Diego Nehab. All rights reserved. + * + * 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. + * ---------------------------------------------------------------------- */ diff --git a/base/rply/rplyfile.h b/base/rply/rplyfile.h new file mode 100644 index 0000000..206e716 --- /dev/null +++ b/base/rply/rplyfile.h @@ -0,0 +1,68 @@ +#ifndef RPLY_FILE_H +#define RPLY_FILE_H +/* ---------------------------------------------------------------------- + * RPly library, read/write PLY files + * Diego Nehab, IMPA + * http://www.impa.br/~diego/software/rply + * + * This library is distributed under the MIT License. See notice + * at the end of this file. + * ---------------------------------------------------------------------- */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* ---------------------------------------------------------------------- + * Opens a PLY file for reading (fails if file is not a PLY file) + * + * file_pointer: FILE * to file open for reading + * error_cb: error callback function + * idata,pdata: contextual information available to users + * + * Returns 1 if successful, 0 otherwise + * ---------------------------------------------------------------------- */ +p_ply ply_open_from_file(FILE *file_pointer, p_ply_error_cb error_cb, + long idata, void *pdata); + +/* ---------------------------------------------------------------------- + * Creates new PLY file + * + * file_pointer: FILE * to a file open for writing + * storage_mode: file format mode + * error_cb: error callback function + * idata,pdata: contextual information available to users + * + * Returns handle to PLY file if successfull, NULL otherwise + * ---------------------------------------------------------------------- */ +p_ply ply_create_to_file(FILE *file_pointer, e_ply_storage_mode storage_mode, + p_ply_error_cb error_cb, long idata, void *pdata); + +#ifdef __cplusplus +} +#endif + +#endif /* RPLY_FILE_H */ + +/* ---------------------------------------------------------------------- + * Copyright (C) 2003-2015 Diego Nehab. All rights reserved. + * + * 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. + * ---------------------------------------------------------------------- */ diff --git a/base/util.c b/base/util.c index 4c641cb..88acd94 100644 --- a/base/util.c +++ b/base/util.c @@ -122,6 +122,9 @@ void write2d(const char *file, const double **X, int nr, int nc, const char* fmt printf("delimited file nor binary file. Abort. \n"); exit(EXIT_FAILURE); } +// Forward declaration for PLY reader +double **read_ply(int *nr, int *nc, const char *file, const char *na); + double ** read2d(int *nr, int *nc, char *mode, const char *file, const char *na){ FILE *fp; char *s,*p,*ext,*dlm=",\t\n"; char *ptr; double *buf=NULL,**X; int m,n,M,N,lim=MAXC; size_t l,sz=INIT; @@ -131,6 +134,7 @@ double ** read2d(int *nr, int *nc, char *mode, const char *file, const char *na) if(!(ext=strrchr(file,'.'))){goto err00;} ext++; if(strcmp(ext,"bin")==0) *mode='b'; if(strcmp(ext,"txt")==0) *mode='t'; + if(strcmp(ext,"ply")==0) *mode='p'; switch(*mode){ case 't': /* Tab-delimited file */ @@ -160,6 +164,10 @@ double ** read2d(int *nr, int *nc, char *mode, const char *file, const char *na) if(1!=fread(&M,si,1,fp)) {goto err04;} if(1!=fread(&N,si,1,fp)) {goto err04;} sz=(size_t)N*M; buf=malloc(sd*sz); if(sz!=fread(buf,sd,sz,fp)) {goto err05;} fclose(fp);break; + + case 'p': /* PLY file */ + X = read_ply(nr, nc, file, na); + return X; case '?': fp=fopen(file,"rb"); if(!fp) goto err01; else goto err06; } *nr=M;*nc=N; X=calloc2d(M,N); diff --git a/makefile b/makefile index 6025542..73d64c9 100644 --- a/makefile +++ b/makefile @@ -1,28 +1,29 @@ CC=gcc -BCPDSRC= register/*.c base/*.c +BCPDSRC= register/*.c base/*.c base/rply/rply.c OMP_PORT= -Xpreprocessor -fopenmp -I/opt/local/include/libomp /opt/local/lib/libomp/libomp.dylib OMP_BREW_ITL= -Xpreprocessor -fopenmp -I/usr/local/include/ /usr/local/lib/libomp.dylib OMP_BREW_ARM= -Xpreprocessor -fopenmp -I/opt/homebrew/include/ /opt/homebrew/lib/libomp.dylib DEBUG= +INCLUDES= -I./base -I./base/rply all: ifeq ($(OPT),-DUSE_OPENMP) ifeq ($(ENV),LINUX) - $(CC) -O3 -fopenmp $(OPT) $(DEBUG) $(BCPDSRC) -o bcpd -lm -llapack + $(CC) -O3 -fopenmp $(OPT) $(DEBUG) $(INCLUDES) $(BCPDSRC) -o bcpd -lm -llapack else ifeq ($(ENV),MINGW32) - $(CC) -O3 -fopenmp $(OPT) $(DEBUG) $(BCPDSRC) win/*.dll -o win/bcpd -lm -DMINGW32 + $(CC) -O3 -fopenmp $(OPT) $(DEBUG) $(INCLUDES) $(BCPDSRC) win/*.dll -o win/bcpd -lm -DMINGW32 else ifeq ($(ENV),HOMEBREW_INTEL) - clang -O3 $(OPT) $(OMP_BREW_ITL) $(DEBUG) $(BCPDSRC) -o bcpd -lm -llapack -Wuninitialized + clang -O3 $(OPT) $(OMP_BREW_ITL) $(DEBUG) $(INCLUDES) $(BCPDSRC) -o bcpd -lm -llapack -Wuninitialized else ifeq ($(ENV),HOMEBREW) - clang -O3 $(OPT) $(OMP_BREW_ARM) $(DEBUG) $(BCPDSRC) -o bcpd -lm -llapack -Wuninitialized + clang -O3 $(OPT) $(OMP_BREW_ARM) $(DEBUG) $(INCLUDES) $(BCPDSRC) -o bcpd -lm -llapack -Wuninitialized else ifeq ($(ENV),MACPORTS) - clang -O3 $(OPT) $(OMP_PORT) $(DEBUG) $(BCPDSRC) -o bcpd -lm -llapack -Wuninitialized + clang -O3 $(OPT) $(OMP_PORT) $(DEBUG) $(INCLUDES) $(BCPDSRC) -o bcpd -lm -llapack -Wuninitialized endif else ifeq ($(OPT),-DNUSE_OPENMP) - $(CC) -O3 $(DEBUG) $(BCPDSRC) -o bcpd -lm -llapack + $(CC) -O3 $(DEBUG) $(INCLUDES) $(BCPDSRC) -o bcpd -lm -llapack else ## default ## - clang -O3 -DUSE_OPENMP $(OMP_BREW_ARM) $(DEBUG) $(BCPDSRC) -o bcpd -lm -llapack + clang -O3 -DUSE_OPENMP $(OMP_BREW_ARM) $(DEBUG) $(INCLUDES) $(BCPDSRC) -o bcpd -lm -llapack endif endif diff --git a/makefile.win b/makefile.win new file mode 100644 index 0000000..cc159b1 --- /dev/null +++ b/makefile.win @@ -0,0 +1,23 @@ +CC = x86_64-w64-mingw32-gcc +CFLAGS = -O3 -DMINGW32 -DUSE_DUMMY_LAPACK -I./base -I./base/rply +LDFLAGS = -lm + +SOURCES = register/main.c register/bcpd.c register/info.c register/norm.c \ + base/util.c base/misc.c base/kdtree.c base/kernel.c base/sampling.c \ + base/sgraph.c base/geokdecomp.c base/plyreader.c base/plywriter.c \ + base/rply/rply.c base/getopt.c base/mt64.c base/median.c base/dijkstra.c \ + base/gaussprod.c base/gramdecomp.c base/dummy_lapack.c base/heap.c \ + base/digamma.c + +OBJECTS = $(SOURCES:.c=.o) + +all: gbcpd.exe + +gbcpd.exe: $(OBJECTS) + $(CC) $(CFLAGS) -o win/gbcpd.exe $(OBJECTS) $(LDFLAGS) + +%.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ + +clean: + rm -f $(OBJECTS) win/gbcpd.exe \ No newline at end of file diff --git a/package_windows.sh b/package_windows.sh new file mode 100755 index 0000000..ac8414b --- /dev/null +++ b/package_windows.sh @@ -0,0 +1,30 @@ +#!/bin/bash +# Script to package Windows version of BCPD + +# Clean up any previous package +rm -rf package + +# Create package directory +mkdir -p package/gbcpd_windows + +# Copy Windows executable and README +cp win/gbcpd.exe package/gbcpd_windows/ +cp win/README.md package/gbcpd_windows/ + +# Create a sample directory with example files +mkdir -p package/gbcpd_windows/samples +if [ -d "data" ]; then + cp data/*.ply package/gbcpd_windows/samples/ 2>/dev/null || echo "No PLY files found in data directory" +fi + +# Create a zip file +cd package +zip -r gbcpd_windows.zip gbcpd_windows/ +cd .. + +# Clean up temporary directory +rm -rf package/gbcpd_windows + +echo "Windows package created at package/gbcpd_windows.zip" +echo "Package contains:" +unzip -l package/gbcpd_windows.zip \ No newline at end of file diff --git a/register/bcpd.c b/register/bcpd.c index 780664f..cb3724c 100644 --- a/register/bcpd.c +++ b/register/bcpd.c @@ -25,6 +25,9 @@ #include #include #include"../base/misc.h" +#ifdef USE_DUMMY_LAPACK +#include"../base/dummy_lapack.h" +#endif #include"../base/lapack.h" #include"../base/kdtree.h" #include"../base/kernel.h" diff --git a/register/info.c b/register/info.c index 155e724..700cdc8 100644 --- a/register/info.c +++ b/register/info.c @@ -46,7 +46,7 @@ void printUsage(void){ printf(" o------------------------------------------------------------------------o\n" ); printf(" | ./bcpd -x -y (+ options) |\n" ); printf(" o------------------------------------------------------------------------o\n" ); - printf(" ** Tab-separated files only. Extension of the input file MUST be '.txt'. \n\n"); + printf(" ** Supported file formats: '.txt' (tab-separated) and '.ply' (PLY format).\n\n"); printf(" OPTIONS: \n" ); printf(" Parameters -w , -l , -g -k \n" ); printf(" Acceleration -J , -K , kdtree: -p, -r \n" ); @@ -61,13 +61,15 @@ void printUsage(void){ printf(" File Output -o , -s \n" ); printf(" Terminal I/O quiet mode: -q, history mode: -h, warning-disabled mode: -W \n\n"); printf(" *1) Parenthesis <...> specifies the argument of an option. \n" ); - printf(" *2) The downsampling option activates BCPD++/GBCPD++. \n\n"); + printf(" *2) The downsampling option activates BCPD++/GBCPD++. \n" ); + printf(" *3) You can also use positional arguments: ./bcpd TARGET SOURCE OUTPUT \n\n"); printf(" DEFAULT: \n" ); printf(" -x X.txt, -y Y.txt, -w 0, -l 2, -b 2, -n 500, -o output_, -c 1e-4, -u e \n" ); printf(" ** All accleration options are disabled unless specified explicitly. \n\n"); printf(" EXAMPLE: \n" ); printf(" o------------------------------------------------------------------------o\n" ); printf(" | ./bcpd -x X.txt -y Y.txt -w0.1 -l2 -b2 -J300 -K80 -p -n90 -c1e-6 -svYP |\n" ); + printf(" | ./bcpd X.txt Y.txt output_ -w0.1 -l2 -b2 -J300 -K80 -p -n90 -c1e-6 -sy |\n" ); printf(" o------------------------------------------------------------------------o\n\n"); printf(" REFERENCE: \n" ); printf(" - Geodesic-Based Bayesian coherent point drift, IEEE TPAMI, 2022 (GBCPD). \n" ); diff --git a/register/main.c b/register/main.c index a496dcc..f97f612 100644 --- a/register/main.c +++ b/register/main.c @@ -23,38 +23,125 @@ #include #include #include +#ifdef _WIN32 +#include +#include +#else #include +#include +#endif #include #include -#include #include"../base/util.h" #include"../base/misc.h" +#ifdef USE_DUMMY_LAPACK +#include"../base/dummy_lapack.h" +#endif #include"../base/kdtree.h" #include"../base/kernel.h" #include"../base/sampling.h" #include"../base/sgraph.h" #include"../base/geokdecomp.h" +#include"../base/plywriter.h" #include"bcpd.h" #include"info.h" #include"norm.h" +#ifdef _WIN32 +// Windows implementation of gettimeofday +int gettimeofday(struct timeval *tv, void *tz) { + FILETIME ft; + unsigned __int64 tmpres = 0; + static int tzflag = 0; + + if (NULL != tv) { + GetSystemTimeAsFileTime(&ft); + + tmpres |= ft.dwHighDateTime; + tmpres <<= 32; + tmpres |= ft.dwLowDateTime; + + // Convert to microseconds + tmpres /= 10; + // Convert file time to unix epoch + tmpres -= 11644473600000000ULL; + + tv->tv_sec = (long)(tmpres / 1000000UL); + tv->tv_usec = (long)(tmpres % 1000000UL); + } + + return 0; +} +#endif + #define SQ(x) ((x)*(x)) void init_genrand64(unsigned long s); enum transpose {ASIS=0,TRANSPOSE=1}; -void save_variable(const char *prefix, const char *suffix,const double *var, int D, int J, char *fmt, int trans){ - int d,j; char fn[256]; double **buf; - strcpy(fn,prefix); strcat(fn,suffix); +void save_variable(const char *prefix, const char *suffix, const double *var, int D, int J, char *fmt, int trans, const pwpm *pm){ + int d,j; char fn[512], *ext, *dir; double **buf; + + // Copy prefix to fn + strcpy(fn, prefix); + + // Check if prefix contains a directory path + dir = strrchr(fn, '/'); + if (dir) { + // Create directory if it doesn't exist + char dirpath[512]; + strncpy(dirpath, fn, dir - fn + 1); + dirpath[dir - fn + 1] = '\0'; + + // Use system to create directory (mkdir -p creates parent directories as needed) + char mkdir_cmd[600]; + sprintf(mkdir_cmd, "mkdir -p %s", dirpath); + system(mkdir_cmd); + } + + // Append suffix + strcat(fn, suffix); + + // Check if we should output as PLY format + int output_ply = 0; + if(strstr(suffix, ".txt") && (ext = strrchr(pm->fn[TARGET], '.'))) { + if(strcmp(ext, ".ply") == 0) { + // Replace .txt with .ply in the output filename + char *txt_ext = strstr(fn, ".txt"); + if(txt_ext) { + strcpy(txt_ext, ".ply"); + output_ply = 1; + } + } + } + if(trans==TRANSPOSE){ buf=calloc2d(J,D); for(j=0;jfn[TARGET],"X.txt"); pm->omg=0.0; pm->cnv=1e-4; pm->K=0; pm->opt=0.0; pm->btn=0.20; pm->bet=2.0; strcpy(pm->fn[SOURCE],"Y.txt"); pm->lmd=2.0; pm->nlp= 500; pm->J=0; pm->dlt=7.0; pm->lim=0.15; pm->eps=1e-3; strcpy(pm->fn[OUTPUT],"output_"); pm->rns=0; pm->llp= 30; pm->G=0; pm->gma=1.0; pm->kpa=ZERO; pm->nrm='e'; @@ -187,6 +308,8 @@ void pw_getopt(pwpm *pm, int argc, char **argv){ int opt; strcpy(pm->fn[FUNC_X],""); pm->dwn[SOURCE]=0; pm->dwr[SOURCE]=0.0f; pm->dwn[TARGET]=0; pm->dwr[TARGET]=0.0f; + + // 处理选项参数 while((opt=getopt(argc,argv,"X:Y:D:z:u:r:w:l:b:k:g:d:e:c:n:N:G:J:K:o:x:y:f:s:hpqvaAtWS1"))!=-1){ switch(opt){ case 'D': scan_dwpm( pm->dwn, pm->dwr,optarg); break; @@ -240,6 +363,25 @@ void pw_getopt(pwpm *pm, int argc, char **argv){ int opt; break; } } + + // 处理位置参数 + for (int i = optind; i < argc; i++) { + switch (positional_args) { + case 0: // 第一个位置参数:目标点云文件 + strcpy(pm->fn[TARGET], argv[i]); + break; + case 1: // 第二个位置参数:源点云文件 + strcpy(pm->fn[SOURCE], argv[i]); + break; + case 2: // 第三个位置参数:输出文件前缀 + strcpy(pm->fn[OUTPUT], argv[i]); + break; + default: + // 忽略多余的位置参数 + break; + } + positional_args++; + } /* acceleration with default parameters */ if(pm->opt&PW_OPT_ACCEL) {pm->J=300;pm->K=70;pm->opt|=PW_OPT_LOCAL;} /* case: save all */ @@ -327,8 +469,8 @@ void fprint_comptime2(FILE *fp, const struct timeval *tv, double *tt, int geok){ int main(int argc, char **argv){ int d,k,l,m,n,D,M,N,lp; char mode; double s,r,Np,sgmX,sgmY,*muX,*muY; double *u,*v,*w,*R,*t,*a,*sgm; - pwpm pm; pwsz sz; double *x,*y,*X,*Y,*wd,**bX,**bY; int *wi; int sd=sizeof(double),si=sizeof(int); FILE *fp; char fn[256]; - int dsz,isz,ysz,xsz; char *ytraj=".optpath.bin",*xtraj=".optpathX.bin"; double tt[7]; struct timeval tv[7]; + pwpm pm; pwsz sz; double *x,*y,*X,*Y,*wd,**bX,**bY; int *wi; int sd=sizeof(double),si=sizeof(int); FILE *fp; char fn[512]; + int dsz,isz,ysz,xsz; char ytraj[512], xtraj[512]; double tt[7]; struct timeval tv[7]; int nx,ny,N0,M0=0; double rx,ry,*T,*X0,*Y0=NULL; double sgmT,*muT; double *pf; double *LQ=NULL,*LQ0=NULL; int *Ux,*Uy; int K; int geok=0; double *x0; @@ -337,6 +479,12 @@ int main(int argc, char **argv){ pw_getopt(&pm,argc,argv); bX=read2d(&N,&D,&mode,pm.fn[TARGET],"NA"); X=calloc(D*N,sd); sz.D=D; bY=read2d(&M,&D,&mode,pm.fn[SOURCE],"NA"); Y=calloc(D*M,sd); + + /* initialize trajectory file paths */ + strcpy(ytraj, pm.fn[OUTPUT]); + strcat(ytraj, ".optpath.bin"); + strcpy(xtraj, pm.fn[OUTPUT]); + strcat(xtraj, ".optpathX.bin"); /* init: random number */ init_genrand64(pm.rns?pm.rns:clock()); /* check dimension */ @@ -420,8 +568,8 @@ int main(int argc, char **argv){ gettimeofday(tv+5,NULL); tt[5]=clock(); /* save interpolated variables */ if(ny){ - save_variable(pm.fn[OUTPUT],"y.interpolated.txt",T, D,M0,"%lf",TRANSPOSE); if(!(pm.opt&PW_OPT_INTPX)) goto skip; - save_variable(pm.fn[OUTPUT],"x.interpolated.txt",x0,D,M0,"%lf",TRANSPOSE); free(x0); skip: free(T); + save_variable(pm.fn[OUTPUT],"y.interpolated.txt",T, D,M0,"%lf",TRANSPOSE,&pm); if(!(pm.opt&PW_OPT_INTPX)) goto skip; + save_variable(pm.fn[OUTPUT],"x.interpolated.txt",x0,D,M0,"%lf",TRANSPOSE,&pm); free(x0); skip: free(T); } /* save correspondence */ if((pm.opt&PW_OPT_SAVEP)|(pm.opt&PW_OPT_SAVEC)|(pm.opt&PW_OPT_SAVEE)) @@ -432,23 +580,23 @@ int main(int argc, char **argv){ /* revert normalization */ denormalize_batch(x,muX,sgmX,y,muY,sgmY,M,M,D,pm.nrm); /* save variables */ - if(pm.opt&PW_OPT_SAVEY) save_variable(pm.fn[OUTPUT],"y.txt",y,D,M,"%lf",TRANSPOSE); - if(pm.opt&PW_OPT_SAVEX) save_variable(pm.fn[OUTPUT],"x.txt",x,D,M,"%lf",TRANSPOSE); - if(pm.opt&PW_OPT_SAVEU) save_variable(pm.fn[OUTPUT],"u.txt",u,D,M,"%lf",TRANSPOSE); - if(pm.opt&PW_OPT_SAVEV) save_variable(pm.fn[OUTPUT],"v.txt",v,D,M,"%lf",TRANSPOSE); - if(pm.opt&PW_OPT_SAVEA) save_variable(pm.fn[OUTPUT],"a.txt",a,M,1,"%e", ASIS); + if(pm.opt&PW_OPT_SAVEY) save_variable(pm.fn[OUTPUT],"y.txt",y,D,M,"%lf",TRANSPOSE,&pm); + if(pm.opt&PW_OPT_SAVEX) save_variable(pm.fn[OUTPUT],"x.txt",x,D,M,"%lf",TRANSPOSE,&pm); + if(pm.opt&PW_OPT_SAVEU) save_variable(pm.fn[OUTPUT],"u.txt",u,D,M,"%lf",TRANSPOSE,&pm); + if(pm.opt&PW_OPT_SAVEV) save_variable(pm.fn[OUTPUT],"v.txt",v,D,M,"%lf",TRANSPOSE,&pm); + if(pm.opt&PW_OPT_SAVEA) save_variable(pm.fn[OUTPUT],"a.txt",a,M,1,"%e", ASIS,&pm); if(pm.opt&PW_OPT_SAVET){ - save_variable(pm.fn[OUTPUT],"s.txt",&s,1,1,"%lf",ASIS); - save_variable(pm.fn[OUTPUT],"R.txt", R,D,D,"%lf",ASIS); - save_variable(pm.fn[OUTPUT],"t.txt", t,D,1,"%lf",ASIS); + save_variable(pm.fn[OUTPUT],"s.txt",&s,1,1,"%lf",ASIS,&pm); + save_variable(pm.fn[OUTPUT],"R.txt", R,D,D,"%lf",ASIS,&pm); + save_variable(pm.fn[OUTPUT],"t.txt", t,D,1,"%lf",ASIS,&pm); } if((pm.opt&PW_OPT_SAVEU)|(pm.opt&PW_OPT_SAVEV)|(pm.opt&PW_OPT_SAVET)){ - save_variable(pm.fn[OUTPUT],"normX.txt",X,D,N,"%lf",TRANSPOSE); - save_variable(pm.fn[OUTPUT],"normY.txt",Y,D,M,"%lf",TRANSPOSE); + save_variable(pm.fn[OUTPUT],"normX.txt",X,D,N,"%lf",TRANSPOSE,&pm); + save_variable(pm.fn[OUTPUT],"normY.txt",Y,D,M,"%lf",TRANSPOSE,&pm); } if((pm.opt&PW_OPT_SAVES)&&(pm.opt&PW_OPT_DBIAS)){ for(m=0;m`: Target point cloud file +- `-s `: Source point cloud file +- `-o `: Output file prefix +- `-w `: Omega (default: 0.1) +- `-l `: Lambda (default: 1.0) +- `-g `: Gamma (default: 1.0) +- `-k `: Kappa (default: 1.0) +- `-b `: Beta (default: 2.0) +- `-n `: Number of iterations (default: 50) +- `-v`: Verbose mode +- `-h`: Show help + +## Supported File Formats + +- PLY (Polygon File Format) + +## Notes + +This Windows version uses dummy LAPACK implementations, so some advanced mathematical operations may not work correctly. For full functionality, use the Linux version with proper LAPACK libraries. + +## Example + +``` +gbcpd.exe bunny_target.ply bunny_source.ply result +``` + +This will generate output files with the prefix "result". + +--- + +# BCPD Windows 版本 (gbcpd.exe) + +这是贝叶斯相干点漂移(Bayesian Coherent Point Drift, BCPD)算法的 Windows 版本。 + +## 使用方法 + +该程序可以通过两种方式使用: + +### 1. 使用位置参数(推荐): + +``` +gbcpd.exe 目标点云.ply 源点云.ply 输出前缀 +``` + +其中: +- `目标点云.ply`:目标点云文件(PLY 格式) +- `源点云.ply`:源点云文件(PLY 格式) +- `输出前缀`:输出文件的前缀 + +### 2. 使用选项: + +``` +gbcpd.exe -t 目标点云.ply -s 源点云.ply -o 输出前缀 [其他选项] +``` + +常用选项: +- `-t <文件>`:目标点云文件 +- `-s <文件>`:源点云文件 +- `-o <前缀>`:输出文件前缀 +- `-w <实数>`:Omega 参数(默认:0.1) +- `-l <实数>`:Lambda 参数(默认:1.0) +- `-g <实数>`:Gamma 参数(默认:1.0) +- `-k <实数>`:Kappa 参数(默认:1.0) +- `-b <实数>`:Beta 参数(默认:2.0) +- `-n <整数>`:迭代次数(默认:50) +- `-v`:详细模式 +- `-h`:显示帮助 + +## 支持的文件格式 + +- PLY(多边形文件格式) + +## 注意事项 + +此 Windows 版本使用了虚拟 LAPACK 实现,因此某些高级数学运算可能无法正确工作。要获得完整功能,请使用带有适当 LAPACK 库的 Linux 版本。 + +## 示例 + +``` +gbcpd.exe bunny_target.ply bunny_source.ply result +``` + +这将生成以 "result" 为前缀的输出文件。 + +--- + +# BCPD Windows バージョン (gbcpd.exe) + +これはベイズコヒーレントポイントドリフト(Bayesian Coherent Point Drift, BCPD)アルゴリズムの Windows バージョンです。 + +## 使用方法 + +このプログラムは2つの方法で使用できます: + +### 1. 位置引数を使用(推奨): + +``` +gbcpd.exe ターゲット.ply ソース.ply 出力プレフィックス +``` + +ここで: +- `ターゲット.ply`:ターゲットポイントクラウドファイル(PLY形式) +- `ソース.ply`:ソースポイントクラウドファイル(PLY形式) +- `出力プレフィックス`:出力ファイルのプレフィックス + +### 2. オプションを使用: + +``` +gbcpd.exe -t ターゲット.ply -s ソース.ply -o 出力プレフィックス [その他のオプション] +``` + +一般的なオプション: +- `-t <ファイル>`:ターゲットポイントクラウドファイル +- `-s <ファイル>`:ソースポイントクラウドファイル +- `-o <プレフィックス>`:出力ファイルプレフィックス +- `-w <実数>`:Omega パラメータ(デフォルト:0.1) +- `-l <実数>`:Lambda パラメータ(デフォルト:1.0) +- `-g <実数>`:Gamma パラメータ(デフォルト:1.0) +- `-k <実数>`:Kappa パラメータ(デフォルト:1.0) +- `-b <実数>`:Beta パラメータ(デフォルト:2.0) +- `-n <整数>`:繰り返し回数(デフォルト:50) +- `-v`:詳細モード +- `-h`:ヘルプを表示 + +## サポートされているファイル形式 + +- PLY(ポリゴンファイル形式) + +## 注意事項 + +この Windows バージョンはダミーの LAPACK 実装を使用しているため、一部の高度な数学演算が正しく機能しない場合があります。完全な機能を使用するには、適切な LAPACK ライブラリを備えた Linux バージョンを使用してください。 + +## 例 + +``` +gbcpd.exe bunny_target.ply bunny_source.ply result +``` + +これにより、"result" をプレフィックスとする出力ファイルが生成されます。 + +--- + +*Special thanks to the original author, Osamu Hirose, for developing the BCPD algorithm.* +*特别感谢原作者 Osamu Hirose 开发 BCPD 算法。* +*オリジナルの BCPD アルゴリズムを開発した広瀬修氏に特別な感謝を捧げます。* \ No newline at end of file diff --git a/win/gbcpd.exe b/win/gbcpd.exe new file mode 100755 index 0000000..0d1f05f Binary files /dev/null and b/win/gbcpd.exe differ