star-mesh

Define and load a binary data format for meshes
git clone git://git.meso-star.fr/star-mesh.git
Log | Files | Refs | README | LICENSE

commit 73e51fcbaa3b74aa42bdb903fa145e699c14dd02
parent 3bc74c4136c1b9185fe1816a7983ddac8cc1ffef
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Wed, 26 Feb 2025 11:41:42 +0100

Add the smsh2vtk utility

Converts a triangular or tetrahedral mesh saved in smsh format to VTK
(legacy) format.

Diffstat:
M.gitignore | 15+++++++--------
MMakefile | 42++++++++++++++++++++++++++++++++++++++++--
Asrc/smsh2vtk.c | 354+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 401 insertions(+), 10 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -1,12 +1,11 @@ +*~ +*.[abod] +.config .gitignore -[Bb]uild* -*.sw[po] -*.[aod] +*.pc +smsh2vtk *.so -*~ +*.sw[po] +tags test* !test*.[ch] -.config -.test -tags -*.pc diff --git a/Makefile b/Makefile @@ -22,7 +22,7 @@ LIBNAME_STATIC = libsmsh.a LIBNAME_SHARED = libsmsh.so LIBNAME = $(LIBNAME_$(LIB_TYPE)) -default: library $(ADDONS) +default: library utils all: default tests ################################################################################ @@ -67,7 +67,42 @@ libsmsh.o: $(OBJ) .c.o: $(CC) $(CFLAGS_LIB) -DSMSH_SHARED_BUILD -c $< -o $@ +################################################################################ +# Util +################################################################################ +UTIL_SRC = src/smsh2vtk.c +UTIL_BIN = $(UTIL_SRC:.c=.b) +UTIL_OBJ = $(UTIL_SRC:.c=.o) +UTIL_DEP = $(UTIL_SRC:.c=.d) +UTIL_EXE = smsh2vtk + PKG_CONFIG_LOCAL = PKG_CONFIG_PATH="./:$${PKG_CONFIG_PATH}" $(PKG_CONFIG) +INCS_UTIL = $$($(PKG_CONFIG_LOCAL) $(PCFLAGS) --cflags rsys smsh-local.pc) +LIBS_UTIL = $$($(PKG_CONFIG_LOCAL) $(PCFLAGS) --libs rsys smsh-local.pc) + +CFLAGS_UTIL = -std=c99 $(CFLAGS_EXE) $(INCS_UTIL) +LDFLAGS_UTIL = $(LDFLAGS_EXE) $(LIBS_UTIL) + +utils: library $(UTIL_DEP) $(UTIL_BIN) + @for src in $(UTIL_SRC); do \ + dep="$${src%*.c}.d"; \ + bin="$${src%*.c}.b"; \ + exe="$$(basename "$${src}" ".c")"; \ + $(MAKE) -fMakefile -f"$${dep}" -f "$${bin}" "$${exe}"; \ + done + +$(UTIL_BIN): + @exe=$$(basename "$@" ".b"); \ + printf '%s: src/%s.o\n' "$${exe}" "$${exe}" > $@ + +$(UTIL_DEP): config.mk smsh-local.pc + $(CC) $(CFLAGS_UTIL) -MM -MT "$(@:.d=.o) $@" $(@:.d=.c) -MF $@ + +$(UTIL_OBJ): config.mk smsh-local.pc + $(CC) $(CFLAGS_UTIL) -c $(@:.o=.c) -o $@ + +$(UTIL_EXE): config.mk smsh-local.pc $(LIBNAME) + $(CC) $(CFLAGS_UTIL) -o $@ src/$@.o $(LDFLAGS_UTIL) ################################################################################ # Miscellaneous @@ -86,13 +121,14 @@ smsh-local.pc: smsh.pc.in -e 's#@RSYS_VERSION@#$(RSYS_VERSION)#g'\ smsh.pc.in > $@ -install: library pkg +install: library utils pkg install() { mode="$$1"; prefix="$$2"; shift 2; \ mkdir -p "$${prefix}"; \ cp "$$@" "$${prefix}"; \ chmod "$${mode}" "$$@"; \ }; \ install 755 "$(DESTDIR)$(LIBPREFIX)" $(LIBNAME); \ + install 755 "$(DESTDIR)$(BINPREFIX)" $(UTIL_EXE); \ install 644 "$(DESTDIR)$(LIBPREFIX)/pkgconfig" smsh.pc; \ install 644 "$(DESTDIR)$(INCPREFIX)/star" src/smsh.h; \ install 644 "$(DESTDIR)$(MANPREFIX)/man5" smsh.5; \ @@ -105,12 +141,14 @@ uninstall: rm -f "$(DESTDIR)$(MANPREFIX)/man5/smsh.5" rm -f "$(DESTDIR)$(PREFIX)/share/doc/star-mesh/COPYING" rm -f "$(DESTDIR)$(PREFIX)/share/doc/star-mesh/README.md" + for i in $(UTIL_EXE); do rm -f "$(DESTDIR)$(BINPREFIX)/$${i}"; done lint: mandoc -T lint -Wbase smsh.5 clean: clean_test rm -f $(DEP) $(OBJ) $(LIBNAME) + rm -f $(UTIL_BIN) $(UTIL_DEP) $(UTIL_OBJ) $(UTIL_EXE) rm -f .config libsmsh.o smsh.pc smsh-local.pc ################################################################################ diff --git a/src/smsh2vtk.c b/src/smsh2vtk.c @@ -0,0 +1,354 @@ +/* Copyright (C) 2020-2023 |Méso|Star> (contact@meso-star.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#define _POSIX_C_SOURCE 200112L /* getopt support */ + +#include "smsh.h" + +#include <rsys/cstr.h> +#include <rsys/mem_allocator.h> +#include <rsys/text_reader.h> + +#include <errno.h> +#include <string.h> /* strerror */ +#include <unistd.h> /* getopt */ + +/* Input arguments */ +struct args { + const char* mesh; /* Tetrahedral mesh */ + const char* output; /* Output file */ +}; +static const struct args ARGS_DEFAULT = {0}; + +/* Command data */ +struct cmd { + struct smsh* mesh; /* Tetrahedral mesh */ + FILE* output; /* Ouput file */ +}; +static const struct cmd CMD_NULL = {0}; + +/******************************************************************************* + * Helper functions + ******************************************************************************/ +static INLINE void +usage(FILE* stream) +{ + fprintf(stream, + "usage: smsh2vtk [-o output] mesh\n"); +} + +static void +args_release(struct args* args) +{ + ASSERT(args); + *args = ARGS_DEFAULT; +} + +static res_T +args_init(struct args* args, const int argc, char** argv) +{ + int opt = 0; + res_T res = RES_OK; + + *args = ARGS_DEFAULT; + + while((opt = getopt(argc, argv, "o:")) != -1) { + switch(opt) { + case 'o': args->output = optarg; break; + default: res = RES_BAD_ARG; + } + if(res != RES_OK) goto error; + } + + if(optind < argc) args->mesh = argv[optind]; + + if(!args->mesh) { + fprintf(stderr, "Mesh is missing\n"); + res = RES_BAD_ARG; + goto error; + } + +exit: + return res; +error: + usage(stderr); + args_release(args); + goto exit; +} + +static res_T +check_mem_leaks(void) +{ + char buffer[128] = {0}; + size_t sz = 0; + + if((sz = mem_allocated_size()) == 0) + return RES_OK; /* No memory leak */ + + size_to_cstr(sz, SIZE_ALL, NULL, buffer, sizeof(buffer)); + fprintf(stderr, "Memory leaks: %s\n", buffer); + return RES_MEM_ERR; +} + +static res_T +write_vtk_header(struct cmd* cmd) +{ + res_T res = RES_OK; + ASSERT(cmd); + + #define FPRINTF(Msg) \ + { if(fprintf(cmd->output, Msg) < 0) goto error; } (void)0 + FPRINTF("# vtk DataFile Version 2.0\n"); + FPRINTF("Volumic mesh\n"); + FPRINTF("ASCII\n"); + #undef FPRINTF + +exit: + return res; +error: + fprintf(stderr, "header write error -- %s\n", strerror(errno)); + res = RES_IO_ERR; + goto exit; +} + +static INLINE int +write_nodes(FILE* stream, const struct smsh_desc* desc) +{ + int err = 0; + size_t i = 0; + ASSERT(stream && desc && desc->dnode == 3); + + #define FPRINTF(...) \ + { if(fprintf(stream, __VA_ARGS__) < 0) goto error; } (void)0 + /* Vertices */ + FPRINTF("POINTS %zu double\n", desc->nnodes); + FOR_EACH(i, 0, desc->nnodes) FPRINTF("%f %f %f\n", SPLIT3(desc->nodes+i*3)); + #undef FPRINTF + +exit: + return err; +error: + err = errno; + goto exit; +} + +static res_T +write_tetrahedra(FILE* stream, const struct smsh_desc* desc) +{ + size_t i = 0; + int err = 0; + + ASSERT(stream && desc && desc->dnode == 3 && desc->dcell == 4); + + #define FPRINTF(...) { \ + if(fprintf(stream, __VA_ARGS__) < 0) { err = errno; goto error; } \ + } (void)0 + + FPRINTF("DATASET UNSTRUCTURED_GRID\n"); + + /* Vertices */ + if((err = write_nodes(stream, desc))) goto error; + + /* Cells */ + FPRINTF("CELLS %zu %zu\n", desc->ncells, desc->ncells*(4+1)); + FOR_EACH(i, 0, desc->ncells) { + FPRINTF("4 %zu %zu %zu %zu\n", SPLIT4(desc->cells+i*4)); + } + + /* Cell types (VTK_TETRA == 10) */ + FPRINTF("CELL_TYPES %zu\n", desc->ncells); + FOR_EACH(i, 0, desc->ncells) FPRINTF("10\n"); + + #undef FPRINTF + +exit: + return err; +error: + goto exit; +} + +static res_T +write_triangles(FILE* stream, const struct smsh_desc* desc) +{ + size_t i = 0; + int err = 0; + + ASSERT(stream && desc && desc->dnode == 3 && desc->dcell == 3); + + #define FPRINTF(...) { \ + if(fprintf(stream, __VA_ARGS__) < 0) { err = errno; goto error; } \ + } (void)0 + + FPRINTF("DATASET POLYDATA\n"); + + /* Vertices */ + if((err == write_nodes(stream, desc))) goto error; + + /* Triangles */ + FPRINTF("POLYGONS %zu %zu\n", desc->ncells, desc->ncells*(3+1)); + FOR_EACH(i, 0, desc->ncells) { + FPRINTF("3 %zu %zu %zu\n", SPLIT3(desc->cells+i*3)); + } + + #undef FPRINTF + +exit: + return err; +error: + goto exit; +} + +static int +write_mesh(struct cmd* cmd) +{ + struct smsh_desc desc = SMSH_DESC_NULL; + int err = 0; + res_T res = RES_OK; + ASSERT(cmd); + + SMSH(get_desc(cmd->mesh, &desc)); + + switch(desc.dcell) { + case 3: err = write_triangles(cmd->output, &desc); break; + case 4: err = write_tetrahedra(cmd->output, &desc); break; + default: FATAL("Unreachable code\n"); + } + if(err != 0) goto error; + +exit: + return res; +error: + fprintf(stderr, "mesh write error -- %s\n", strerror(err)); + res = RES_IO_ERR; + goto exit; +} + +static res_T +setup_mesh(struct cmd* cmd, const struct args* args) +{ + struct smsh_create_args create_args = SMSH_CREATE_ARGS_DEFAULT; + struct smsh_load_args load_args = SMSH_LOAD_ARGS_NULL; + struct smsh_desc desc = SMSH_DESC_NULL; + res_T res = RES_OK; + ASSERT(cmd && args); + + create_args.verbose = 1; + if((res = smsh_create(&create_args, &cmd->mesh)) != RES_OK) goto error; + + load_args.path = args->mesh; + if((res = smsh_load(cmd->mesh, &load_args)) != RES_OK) goto error; + + if((res = smsh_get_desc(cmd->mesh, &desc)) != RES_OK) goto error; + + if(desc.dnode != 3 || (desc.dcell != 3 && desc.dcell != 4)) { + fprintf(stderr, + "expecting a tetrahedral mesh or a triangle mesh " + "-- dcell = %u, dnode = %u\n", + desc.dcell, desc.dnode); + res = RES_BAD_ARG; + goto error; + } + +exit: + return res; +error: + if(cmd->mesh) { SMSH(ref_put(cmd->mesh)); cmd->mesh = NULL; } + goto exit; +} + +static res_T +setup_output(struct cmd* cmd, const struct args* args) +{ + res_T res = RES_OK; + ASSERT(cmd && args); + + if(!args->output) { + cmd->output = stdout; + } else if((cmd->output = fopen(args->output, "w")) == NULL) { + fprintf(stderr, "error opening output file '%s' -- %s\n", + args->output, strerror(errno)); + res = RES_IO_ERR; + goto error; + } + +exit: + return res; +error: + if(cmd->output) { CHK(fclose(cmd->output) == 0); cmd->output = NULL; } + goto exit; +} + +static res_T +cmd_init(struct cmd* cmd, const struct args* args) +{ + res_T res = RES_OK; + ASSERT(cmd && args); + + if((res = setup_mesh(cmd, args)) != RES_OK) goto error; + if((res = setup_output(cmd, args)) != RES_OK) goto error; + +exit: + return res; +error: + goto exit; +} + +static void +cmd_release(struct cmd* cmd) +{ + ASSERT(cmd); + if(cmd->mesh) SMSH(ref_put(cmd->mesh)); + if(cmd->output && cmd->output != stdout) CHK(fclose(cmd->output) == 0); +} + +static res_T +cmd_run(struct cmd* cmd) +{ + res_T res = RES_OK; + ASSERT(cmd); + + if((res = write_vtk_header(cmd)) != RES_OK) goto error; + if((res = write_mesh(cmd)) != RES_OK) goto error; + +exit: + return res; +error: + goto exit; +} + +/******************************************************************************* + * The program + ******************************************************************************/ +int +main(int argc, char** argv) +{ + struct args args = ARGS_DEFAULT; + struct cmd cmd = CMD_NULL; + int err = 0; + res_T res = RES_OK; + + if((res = args_init(&args, argc, argv)) != RES_OK) goto error; + if((res = cmd_init(&cmd, &args)) != RES_OK) goto error; + if((res = cmd_run(&cmd)) != RES_OK) goto error; + +exit: + args_release(&args); + cmd_release(&cmd); + if((res = check_mem_leaks()) != RES_OK && !err) err = 1; + return err; +error: + err = 1; + goto exit; +}