commit dcd5347ba86979bb53978c38b60875177e83f141
parent cbf9aaae703b390303e4e46304df49060799a833
Author: Vincent Forest <vincent.forest@meso-star.com>
Date: Fri, 9 May 2025 15:36:57 +0200
Merge branch 'release_0.2'
Diffstat:
| M | .gitignore | | | 17 | +++++++++-------- |
| M | Makefile | | | 183 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------ |
| M | README.md | | | 24 | +++++++++++++++++++----- |
| M | config.mk | | | 14 | +++++++++----- |
| A | doc/smsh-desc.1 | | | 64 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | doc/smsh.5 | | | 102 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | doc/smsh2vtk.1 | | | 69 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | doc/vtk-data.1 | | | 106 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| D | make.sh | | | 70 | ---------------------------------------------------------------------- |
| D | smsh.5 | | | 102 | ------------------------------------------------------------------------------- |
| A | src/smsh-desc.c | | | 209 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| M | src/smsh.c | | | 2 | +- |
| M | src/smsh.h | | | 2 | +- |
| A | src/smsh2vtk.c | | | 355 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| M | src/smsh_c.h | | | 2 | +- |
| M | src/smsh_log.c | | | 2 | +- |
| M | src/smsh_log.h | | | 2 | +- |
| M | src/test_smsh.c | | | 2 | +- |
| M | src/test_smsh_load.c | | | 2 | +- |
| A | src/vtk-data.c | | | 284 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
20 files changed, 1360 insertions(+), 253 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -1,12 +1,13 @@
+*~
+*.[abod]
+.config
.gitignore
-[Bb]uild*
-*.sw[po]
-*.[aod]
+*.pc
+smsh2vtk
+smsh-desc
*.so
-*~
+*.sw[po]
+tags
test*
!test*.[ch]
-.config
-.test
-tags
-*.pc
+vtk-data
diff --git a/Makefile b/Makefile
@@ -1,4 +1,4 @@
-# Copyright (C) 2020-2023 |Méso|Star> (contact@meso-star.com)
+# Copyright (C) 2020-2023, 2025 |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
@@ -14,7 +14,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
.POSIX:
-.SUFFIXES: # Clean up default inference rules
+.SUFFIXES: .b .c .d .o
include config.mk
@@ -22,14 +22,20 @@ LIBNAME_STATIC = libsmsh.a
LIBNAME_SHARED = libsmsh.so
LIBNAME = $(LIBNAME_$(LIB_TYPE))
+default: library utils
+all: default tests
+
################################################################################
-# Library building
+# Library
################################################################################
SRC = src/smsh.c src/smsh_log.c
OBJ = $(SRC:.c=.o)
DEP = $(SRC:.c=.d)
-build_library: .config $(DEP)
+CFLAGS_LIB = -std=c89 $(CFLAGS_SO) $(INCS) -DSMSH_SHARED_BUILD
+LDFLAGS_LIB = $(LDFLAGS_SO) $(LIBS)
+
+library: .config $(DEP)
@$(MAKE) -fMakefile $$(for i in $(DEP); do echo -f $${i}; done) \
$$(if [ -n "$(LIBNAME)" ]; then\
echo "$(LIBNAME)";\
@@ -40,7 +46,7 @@ build_library: .config $(DEP)
$(DEP) $(OBJ): config.mk
$(LIBNAME_SHARED): $(OBJ)
- $(CC) $(CFLAGS_SO) $(RSYS_CFLAGS) -o $@ $(OBJ) $(LDFLAGS_SO) $(RSYS_LIBS)
+ $(CC) $(CFLAGS_LIB) -o $@ $(OBJ) $(LDFLAGS_LIB)
$(LIBNAME_STATIC): libsmsh.o
$(AR) -rc $@ $?
@@ -51,19 +57,55 @@ libsmsh.o: $(OBJ)
$(OBJCOPY) $(OCPFLAGS) $@
.config: config.mk
- @if ! $(PKG_CONFIG) --atleast-version $(RSYS_VERSION) rsys; then\
- echo "rsys $(RSYS_VERSION) not found" >&2; exit 1; fi
+ $(PKG_CONFIG) --atleast-version $(RSYS_VERSION) rsys
@echo "config done" > $@
.SUFFIXES: .c .d .o
.c.d:
- @$(CC) $(CFLAGS_SO) $(RSYS_CFLAGS) -MM -MT "$(@:.d=.o) $@" $< -MF $@
+ @$(CC) $(CFLAGS_LIB) -MM -MT "$(@:.d=.o) $@" $< -MF $@
.c.o:
- $(CC) $(CFLAGS_SO) $(RSYS_CFLAGS) -DSMSH_SHARED_BUILD -c $< -o $@
+ $(CC) $(CFLAGS_LIB) -DSMSH_SHARED_BUILD -c $< -o $@
################################################################################
-# Installation
+# Util
+################################################################################
+UTIL_SRC = src/smsh2vtk.c src/smsh-desc.c src/vtk-data.c
+UTIL_BIN = $(UTIL_SRC:.c=.b)
+UTIL_OBJ = $(UTIL_SRC:.c=.o)
+UTIL_DEP = $(UTIL_SRC:.c=.d)
+UTIL_EXE = smsh2vtk smsh-desc vtk-data
+
+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
################################################################################
pkg:
sed -e 's#@PREFIX@#$(PREFIX)#g' \
@@ -79,37 +121,44 @@ smsh-local.pc: smsh.pc.in
-e 's#@RSYS_VERSION@#$(RSYS_VERSION)#g'\
smsh.pc.in > $@
-install: build_library pkg
- @$(SHELL) make.sh install "$(DESTDIR)$(PREFIX)/lib" $(LIBNAME)
- @$(SHELL) make.sh install "$(DESTDIR)$(PREFIX)/lib/pkgconfig" smsh.pc
- @$(SHELL) make.sh install "$(DESTDIR)$(PREFIX)/include/star" src/smsh.h
- @$(SHELL) make.sh install "$(DESTDIR)$(PREFIX)/share/doc/star-mesh" COPYING README.md
- @$(SHELL) make.sh install "$(DESTDIR)$(PREFIX)/share/man/man5" smsh.5
+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)/man1" doc/smsh2vtk.1; \
+ install 644 "$(DESTDIR)$(MANPREFIX)/man1" doc/smsh-desc.1; \
+ install 644 "$(DESTDIR)$(MANPREFIX)/man1" doc/vtk-data.1; \
+ install 644 "$(DESTDIR)$(MANPREFIX)/man5" doc/smsh.5; \
+ install 644 "$(DESTDIR)$(PREFIX)/share/doc/star-mesh" COPYING README.md
uninstall:
- rm -f "$(DESTDIR)$(PREFIX)/lib/$(LIBNAME)"
- rm -f "$(DESTDIR)$(PREFIX)/lib/pkgconfig/smsh.pc"
+ rm -f "$(DESTDIR)$(LIBPREFIX)/$(LIBNAME)"
+ rm -f "$(DESTDIR)$(LIBPREFIX)/pkgconfig/smsh.pc"
+ rm -f "$(DESTDIR)$(INCPREFIX)/star/smsh.h"
+ rm -f "$(DESTDIR)$(MANPREFIX)/man1/smsh2vtk.1"
+ rm -f "$(DESTDIR)$(MANPREFIX)/man1/smsh-desc.1"
+ rm -f "$(DESTDIR)$(MANPREFIX)/man1/vtk-data.1"
+ 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"
- rm -f "$(DESTDIR)$(PREFIX)/include/star/smsh.h"
- rm -f "$(DESTDIR)$(PREFIX)/share/man/man5/smsh.5"
+ for i in $(UTIL_EXE); do rm -f "$(DESTDIR)$(BINPREFIX)/$${i}"; done
-################################################################################
-# Miscellaneous targets
-################################################################################
-all: build_library build_tests
+lint:
+ mandoc -T lint -Wbase doc/smsh.5
+ mandoc -T lint -Wbase doc/smsh2vtk.1 || [ $$? -eq 1 ];
+ mandoc -T lint -Wbase doc/smsh-desc.1 || [ $$? -eq 1 ];
+ mandoc -T lint -Wbase doc/vtk-data.1
clean: clean_test
- rm -f $(OBJ) $(TEST_OBJ) $(LIBNAME)
- rm -f .config .test libsmsh.o smsh.pc smsh-local.pc
- rm -f test_file.smsh
-
-distclean: clean
- rm -f $(DEP) $(TEST_DEP)
-
-lint:
- shellcheck -o all make.sh
- mandoc -T lint -Wbase smsh.5
+ 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
################################################################################
# Tests
@@ -117,30 +166,52 @@ lint:
TEST_SRC = src/test_smsh.c src/test_smsh_load.c
TEST_OBJ = $(TEST_SRC:.c=.o)
TEST_DEP = $(TEST_SRC:.c=.d)
-
-PKG_CONFIG_LOCAL = PKG_CONFIG_PATH="./:$${PKG_CONFIG_PATH}" $(PKG_CONFIG)
-SMSH_CFLAGS = $$($(PKG_CONFIG_LOCAL) $(PCFLAGS) --cflags smsh-local.pc)
-SMSH_LIBS = $$($(PKG_CONFIG_LOCAL) $(PCFLAGS) --libs smsh-local.pc)
-
-test: build_tests
- @$(SHELL) make.sh run_test $(TEST_SRC)
-
-build_tests: build_library $(TEST_DEP) .test
- @$(MAKE) -fMakefile -f.test $$(for i in $(TEST_DEP); do echo -f"$${i}"; done) test_bin
-
-.test: Makefile make.sh
- @echo "Setup tests"
- @$(SHELL) make.sh config_test $(TEST_SRC) > $@
-
-clean_test:
- @$(SHELL) make.sh clean_test $(TEST_SRC)
+TEST_BIN = $(TEST_SRC:.c=.b)
+
+INCS_TEST = $$($(PKG_CONFIG_LOCAL) $(PCFLAGS) --cflags rsys smsh-local.pc)
+LIBS_TEST = $$($(PKG_CONFIG_LOCAL) $(PCFLAGS) --libs rsys smsh-local.pc)
+
+CFLAGS_TEST = -std=c89 $(CFLAGS_EXE) $(INCS_TEST)
+LDFLAGS_TEST = $(LDFLAGS_EXE) $(LIBS_TEST)
+
+test: tests
+ @err=0; \
+ for i in $(TEST_SRC); do \
+ test="$$(basename "$${i}" ".c")"; \
+ if "./$${test}" > /dev/null 2>&1; then \
+ printf '%s\n' "$${test}"; \
+ else \
+ >&2 printf '%s: error %s\n' "$${test}" "$$?"; \
+ err=$$((err+1)); \
+ fi \
+ done; \
+ [ "$${err}" -eq 0 ]
+
+tests: library $(TEST_DEP) $(TEST_BIN)
+ @$(MAKE) -fMakefile \
+ $$(for i in $(TEST_DEP); do echo -f"$${i}"; done) \
+ $$(for i in $(TEST_BIN); do echo -f"$${i}"; done) \
+ test_bin
+
+.c.b:
+ @{ \
+ bin="$$(basename "$<" ".c")"; \
+ printf '%s: %s\n' "$${bin}" $(<:.c=.o); \
+ printf 'test_bin: %s\n' "$${bin}"; \
+ } > $@
$(TEST_DEP): config.mk smsh-local.pc
- @$(CC) $(CFLAGS_EXE) $(RSYS_CFLAGS) $(SMSH_CFLAGS) \
- -MM -MT "$(@:.d=.o) $@" $(@:.d=.c) -MF $@
+ @$(CC) $(CFLAGS_TEST) -MM -MT "$(@:.d=.o) $@" $(@:.d=.c) -MF $@
$(TEST_OBJ): config.mk smsh-local.pc
- $(CC) $(CFLAGS_EXE) $(RSYS_CFLAGS) $(SMSH_CFLAGS) -c $(@:.o=.c) -o $@
+ $(CC) $(CFLAGS_TEST) -c $(@:.o=.c) -o $@
-test_smsh test_smsh_load: config.mk smsh-local.pc $(LIBNAME)
- $(CC) $(CFLAGS_EXE) -o $@ src/$@.o $(LDFLAGS_EXE) $(SMSH_LIBS) $(RSYS_LIBS)
+test_smsh \
+test_smsh_load \
+: config.mk smsh-local.pc $(LIBNAME)
+ $(CC) $(CFLAGS_TEST) -o $@ src/$@.o $(LDFLAGS_TEST)
+
+clean_test:
+ rm -f $(TEST_BIN) $(TEST_DEP) $(TEST_OBJ)
+ rm -f test_file.smsh
+ for i in $(TEST_SRC); do rm -f "$$(basename "$${i}" ".c")"; done
diff --git a/README.md b/README.md
@@ -19,12 +19,26 @@ Edit config.mk as needed, then run:
## Release notes
+### Version 0.2
+
+- Add the smsh-desc utility.
+ It prints the descriptor of a smsh file
+- Add the smsh2vtk utility.
+ It converts triangular or tetrahedral meshes saved in smshs format to
+ VTK (legacy) format.
+- Add the vtk-data utility.
+ It formats a list of doubles as cell data in the legacy VTK format.
+ It is then possible to attach data to a VTK mesh such as that produced
+ by the smsh2vtk tool.
+- Improves the building system.
+ Simplify it by doing everything in one place (the Makefile).
+ Add macros to control installation subdirectories.
+
### Version 0.1
-- Make memory mapping optional. By default, data is now loaded into
- memory. Memory mapping becomes an option of the load functions,
- (forbidden on stdin). As a consequence, this commit introduces API
- breaks.
+- Make memory mapping optional.
+ By default, data is now loaded into memory. Memory mapping becomes an
+ option of the load functions, (forbidden on stdin).
- Write the man page directly in mdoc's roff macros, instead of using
the intermediate scdoc source.
- Replace CMake by Makefile as build system.
@@ -35,7 +49,7 @@ Edit config.mk as needed, then run:
## License
-Copyright (C) 2020-2023 |Méso|Star> (contact@meso-star.com)
+Copyright (C) 2020-2023, 2025 |Méso|Star> (contact@meso-star.com)
Star-Mesh is free software released under the GPL v3+ license: GNU GPL
version 3 or later. You are welcome to redistribute it under certain
diff --git a/config.mk b/config.mk
@@ -1,4 +1,4 @@
-VERSION = 0.1.0
+VERSION = 0.2.0
PREFIX = /usr/local
LIB_TYPE = SHARED
@@ -7,6 +7,11 @@ LIB_TYPE = SHARED
BUILD_TYPE = RELEASE
#BUILD_TYPE = DEBUG
+BINPREFIX = $(PREFIX)/bin
+LIBPREFIX = $(PREFIX)/lib
+INCPREFIX = $(PREFIX)/include
+MANPREFIX = $(PREFIX)/share/man
+
################################################################################
# Tools
################################################################################
@@ -24,9 +29,9 @@ PCFLAGS_SHARED =
PCFLAGS_STATIC = --static
PCFLAGS = $(PCFLAGS_$(LIB_TYPE))
-RSYS_VERSION=0.14
-RSYS_CFLAGS = $$($(PKG_CONFIG) $(PCFLAGS) --cflags rsys)
-RSYS_LIBS = $$($(PKG_CONFIG) $(PCFLAGS) --libs rsys)
+RSYS_VERSION = 0.14
+INCS = $$($(PKG_CONFIG) $(PCFLAGS) --cflags rsys)
+LIBS = $$($(PKG_CONFIG) $(PCFLAGS) --libs rsys)
################################################################################
# Compilation options
@@ -47,7 +52,6 @@ CFLAGS_HARDENED =\
-fstack-protector-strong
CFLAGS_COMMON =\
- -std=c89\
-pedantic\
-fvisibility=hidden\
-fstrict-aliasing\
diff --git a/doc/smsh-desc.1 b/doc/smsh-desc.1
@@ -0,0 +1,64 @@
+.\" Copyright (C) 2020-2023, 2025 |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/>.
+.Dd May 9, 2025
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.Dt SMSH-DESC 1
+.Os
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.Sh NAME
+.Nm smsh-desc
+.Nd
+.Xr smsh 5
+file descriptor
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.Sh SYNOPSIS
+.Nm
+.Op Fl cdnpt
+.Ar mesh
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.Sh DESCRIPTION
+Print the pagesize on which the
+.Xr smsh 5
+mesh data is aligned, as well as the number and dimension of nodes and
+cells.
+.Pp
+The descritpion is printed on stdout with a message of the form:
+.Bd -literal -offset Ds
+"%zu %zu %zu %u %u %s", pagesz, #nodes, #cells, Dnode, Dcell, mesh
+.Ed
+.Pp
+When any option is specified,
+.Nm
+reports only the information requested by the specified options.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl c
+print cells count.
+.It Fl d
+print node dimension.
+.It Fl n
+print node count.
+.It Fl p
+print data alignment.
+.It Fl t
+print cell dimension.
+.El
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.Sh EXIT STATUS
+.Ex -std
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.Sh SEE ALSO
+.Xr smsh 5
diff --git a/doc/smsh.5 b/doc/smsh.5
@@ -0,0 +1,102 @@
+.\" Copyright (C) 2020-2023, 2025, 2025 |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/>.
+.Dd July 26, 2023
+.Dt SMSH 5
+.Os
+.Sh NAME
+.Nm smsh
+.Nd Star-Mesh file format
+.Sh DESCRIPTION
+.Nm
+is a binary file format that describes an indexed mesh
+.Pq surface or volume .
+Only the geometric data of the mesh is stored; no additional properties are
+attached to its nodes or cells.
+.Pp
+A
+.Nm
+file begins with a header that describes the layout of the data, followed by
+the geometric data itself, i.e. the list of nodes and the list of cells.
+.Pp
+The header consists of 5 integers.
+The first integer is a power of two
+.Pq usually 4096
+that defines the size of the memory page in bytes
+.Pq Va pagesize
+on which the list of nodes and the list of cells are aligned.
+By aligning data to
+.Va pagesize ,
+and depending on system requirements, memory mapping can be used to
+automatically load/unload pages on demand
+.Pq see Xr mmap 2 .
+The remaining integers store the number of nodes
+.Pq Va #nodes
+and the number of cells
+.Pq Va #cells ,
+followed by the size of a node
+.Pq Va dimnode
+and the size of a cell
+.Pq Va dimcell ,
+respectively.
+.Pp
+Fill bytes follow the file header to align nodes to
+.Va pagesize .
+The nodes are then listed with a list of
+.Va #nodes dimnode
+double-precision floating-point numbers per node, where
+.Va #nodes
+is the number
+of mesh nodes and
+.Va dimnode
+is the number of floating-point numbers per node.
+Additional fill bytes are added after the node list to align the list of
+upcoming cells to
+.Va pagesize .
+The cells are then listed using
+.Va dimcell
+64-bit unsigned integers per node where each
+integer indexes a node in the previously defined list of nodes
+.Pq indexing starts at 0 .
+Finally, fill bytes are added to align the overall file size to
+.Va pagesize .
+.Pp
+Data are encoded with respect to the little endian bytes ordering, i.e. least
+significant bytes are stored first.
+.Pp
+The file format is as follows:
+.Bl -column (pagesize) (::=) ()
+.It Ao Va smsh Ac Ta ::= Ta Ao Va pagesize Ac Ao Va #nodes Ac Ao Va #cells Ac Ao Va dimnode Ac Ao Va dimcel Ac
+.It Ta Ta Aq Va padding
+.It Ta Ta Aq Va nodes
+.It Ta Ta Aq Va padding
+.It Ta Ta Aq Va cells
+.It Ta Ta Aq Va padding
+.It Ao Va pagesize Ac Ta ::= Ta Vt uint64_t
+.It Ao Va #nodes Ac Ta ::= Ta Vt uint64_t
+.It Ao Va #cells Ac Ta ::= Ta Vt uint64_t
+.It Ao Va dimnode Ac Ta ::= Ta Vt uint32_t
+.It Ao Va dimcell Ac Ta ::= Ta Vt uint32_t
+.It \ Ta Ta
+.It Ao Va padding Ac Ta ::= Ta Op Vt int8_t ...
+# Ensure alignment on
+.Va pagesize
+.It \ Ta Ta
+.It Ao Va nodes Ac Ta ::= Ta Ao Va node-pos Ac Va ...
+.It Ao Va cells Ac Ta ::= Ta Ao Va cell-ids Ac Va ...
+.It Ao Va node-pos Ac Ta ::= Ta Vt double ...
+.It Ao Va cell-ids Ac Ta ::= Ta Vt uint64_t ...
+.El
+.Sh SEE ALSO
+.Xr mmap 2
diff --git a/doc/smsh2vtk.1 b/doc/smsh2vtk.1
@@ -0,0 +1,69 @@
+.\" Copyright (C) 2020-2023, 2025 |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/>.
+.Dd March 1, 2025
+.\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.Dt SMSH2VTK 1
+.Os
+.\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.Sh NAME
+.Nm smsh2vtk
+.Nd convert
+.Xr smsh 5
+mesh to VTK format
+.\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.Sh SYNOPSIS
+.Nm
+.Op Fl o Ar output
+.Ar mesh
+.\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.Sh DESCRIPTION
+.Nm
+converts a triangular or tetrahedral mesh saved in
+.Xr smsh 5
+format to legacy VTK format.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl o Ar output
+Output file.
+If not defined, output data is written to standard output.
+.El
+.\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.Sh EXIT STATUS
+.Ex -std
+.\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.Sh EXAMPLES
+Convert the
+.Xr smsh 5
+.Pa input.smsh
+mesh to VTK format and saves the results in the
+.Pa output.vtk
+file:
+.Bd -literal -offset Ds
+smsh2vtk -o output.vtk input.smsh
+.Ed
+.\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.Sh SEE ALSO
+.Xr smsh 5
+.\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.Sh STANDARDS
+.Rs
+.%B The VTK User's Guide
+.%O Simple Legacy Formats
+.%I Kitware, Inc
+.%N 11
+.%D 2010
+.%P 470--482
+.Re
diff --git a/doc/vtk-data.1 b/doc/vtk-data.1
@@ -0,0 +1,106 @@
+.\" Copyright (C) 2020-2023, 2025 |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/>.
+.Dd May 9, 2025
+.\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.Dt VTK-CELL-DATA 1
+.Os
+.\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.Sh NAME
+.Nm vtk-data
+.Nd transforms a list of values into cell data in VTK format
+.\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.Sh SYNOPSIS
+.Nm
+.Op Fl n Ar name No ...
+.Op Fl o Ar output
+.Fl c Ar cell_count
+.Op Ar data
+.\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.Sh DESCRIPTION
+.Nm
+formats a list of double precision values in cell data sets as defined in the
+legacy VTK format.
+To describe a valid VTK file, the result must then be concatenated with a VTK
+file storing the geometry of the cells to which this data corresponds.
+.Pp
+As input,
+.Nm
+reads a set of
+.Ar cell_count
+floating-point ASCII values from
+.Ar data
+or from standard input if
+.Ar data
+is not specified.
+This continues until no more data is supplied or 8 sets have been read.
+Each set thus represents a data set associated with each cell.
+If a set doesn't contain enough data
+.Pq i.e. less than Ar cell_count ,
+an error is returned.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl n Ar name
+Name of a data set.
+It cannot contain spaces or tabs.
+This option can be repeated as many times as there are data sets to be
+processed.
+By default, the name of each dataset is
+.Qq data Ns Ar i ,
+with
+.Ar i
+in
+.Bq 0,7
+the dataset index.
+.It Fl o Ar output
+Output file.
+If not defined, VTK output data is written to the standard output.
+.It Fl c Ar cell_count
+Number of cells, i.e. number of floating-point values provided per data set.
+.El
+.\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.Sh EXIT STATUS
+.Ex -std
+.\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.Sh EXAMPLES
+Formats a list of 200 floating-point ASCII values read from the
+.Pa data.txt
+file into two sets of
+.Ar 100
+values, one value per cell.
+Name the first set of data per cell
+.Qq temperature ,
+and the second
+.Qq flux .
+Output VTK data are written to
+.Pa data.vtk .
+Next, concatenate the cell data with their corresponding geometry stored in the
+.Pa mesh.vtk
+file, and thus define the complete VTK file
+.Pa results.vtk .
+.Bd -literal -offset Ds
+vtk-cell-data -c 100 -n temperature -n flux -o data.vtk data.txt
+cat mesh.vtk data.vtk > result.vtk
+.Ed
+.\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.Sh STANDARDS
+.Rs
+.%B The VTK User's Guide
+.%O Simple Legacy Formats
+.%I Kitware, Inc
+.%N 11
+.%D 2010
+.%P 470--482
+.Re
diff --git a/make.sh b/make.sh
@@ -1,70 +0,0 @@
-#!/bin/sh
-
-# 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/>.
-
-set -e
-
-config_test()
-{
- for i in "$@"; do
- test=$(basename "${i}" ".c")
- test_list="${test_list} ${test}"
- printf "%s: %s\n" "${test}" "src/${test}.o"
- done
- printf "test_bin: %s\n" "${test_list}"
-}
-
-run_test()
-{
- for i in "$@"; do
- test=$(basename "${i}" ".c")
-
- printf "%s " "${test}"
- if "./${test}" > /dev/null 2>&1; then
- printf "\033[1;32mOK\033[m\n"
- else
- printf "\033[1;31mError\033[m\n"
- fi
- done 2> /dev/null
-}
-
-clean_test()
-{
- for i in "$@"; do
- rm -f "$(basename "${i}" ".c")"
- done
-}
-
-install()
-{
- prefix=$1
- shift 1
-
- mkdir -p "${prefix}"
-
- for i in "$@"; do
- dst="${prefix}/${i##*/}"
-
- if cmp -s "${i}" "${dst}"; then
- printf "Up to date %s\n" "${dst}"
- else
- printf "Installing %s\n" "${dst}"
- cp "${i}" "${prefix}"
- fi
- done
-}
-
-"$@"
diff --git a/smsh.5 b/smsh.5
@@ -1,102 +0,0 @@
-.\" 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/>.
-.Dd July 26, 2023
-.Dt SMSH 5
-.Os
-.Sh NAME
-.Nm smsh
-.Nd Star-Mesh file format
-.Sh DESCRIPTION
-.Nm
-is a binary file format that describes an indexed mesh
-.Pq surface or volume .
-Only the geometric data of the mesh is stored; no additional properties are
-attached to its nodes or cells.
-.Pp
-A
-.Nm
-file begins with a header that describes the layout of the data, followed by
-the geometric data itself, i.e. the list of nodes and the list of cells.
-.Pp
-The header consists of 5 integers.
-The first integer is a power of two
-.Pq usually 4096
-that defines the size of the memory page in bytes
-.Pq Va pagesize
-on which the list of nodes and the list of cells are aligned.
-By aligning data to
-.Va pagesize ,
-and depending on system requirements, memory mapping can be used to
-automatically load/unload pages on demand
-.Pq see Xr mmap 2 .
-The remaining integers store the number of nodes
-.Pq Va #nodes
-and the number of cells
-.Pq Va #cells ,
-followed by the size of a node
-.Pq Va dimnode
-and the size of a cell
-.Pq Va dimcell ,
-respectively.
-.Pp
-Fill bytes follow the file header to align nodes to
-.Va pagesize .
-The nodes are then listed with a list of
-.Va #nodes dimnode
-double-precision floating-point numbers per node, where
-.Va #nodes
-is the number
-of mesh nodes and
-.Va dimnode
-is the number of floating-point numbers per node.
-Additional fill bytes are added after the node list to align the list of
-upcoming cells to
-.Va pagesize .
-The cells are then listed using
-.Va dimcell
-64-bit unsigned integers per node where each
-integer indexes a node in the previously defined list of nodes
-.Pq indexing starts at 0 .
-Finally, fill bytes are added to align the overall file size to
-.Va pagesize .
-.Pp
-Data are encoded with respect to the little endian bytes ordering, i.e. least
-significant bytes are stored first.
-.Pp
-The file format is as follows:
-.Bl -column (pagesize) (::=) ()
-.It Ao Va smsh Ac Ta ::= Ta Ao Va pagesize Ac Ao Va #nodes Ac Ao Va #cells Ac Ao Va dimnode Ac Ao Va dimcel Ac
-.It Ta Ta Aq Va padding
-.It Ta Ta Aq Va nodes
-.It Ta Ta Aq Va padding
-.It Ta Ta Aq Va cells
-.It Ta Ta Aq Va padding
-.It Ao Va pagesize Ac Ta ::= Ta Vt uint64_t
-.It Ao Va #nodes Ac Ta ::= Ta Vt uint64_t
-.It Ao Va #cells Ac Ta ::= Ta Vt uint64_t
-.It Ao Va dimnode Ac Ta ::= Ta Vt uint32_t
-.It Ao Va dimcell Ac Ta ::= Ta Vt uint32_t
-.It \ Ta Ta
-.It Ao Va padding Ac Ta ::= Ta Op Vt int8_t ...
-# Ensure alignment on
-.Va pagesize
-.It \ Ta Ta
-.It Ao Va nodes Ac Ta ::= Ta Ao Va node-pos Ac Va ...
-.It Ao Va cells Ac Ta ::= Ta Ao Va cell-ids Ac Va ...
-.It Ao Va node-pos Ac Ta ::= Ta Vt double ...
-.It Ao Va cell-ids Ac Ta ::= Ta Vt uint64_t ...
-.El
-.Sh SEE ALSO
-.Xr mmap 2
diff --git a/src/smsh-desc.c b/src/smsh-desc.c
@@ -0,0 +1,209 @@
+/* Copyright (C) 2020-2023, 2025 |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/str.h>
+
+#include <errno.h>
+#include <unistd.h> /* getopt */
+
+enum query_flag {
+ QUERY_DCELL = BIT(0),
+ QUERY_DNODE = BIT(1),
+ QUERY_NCELLS = BIT(2),
+ QUERY_NNODES = BIT(3),
+ QUERY_PAGESIZE = BIT(4),
+ QUERY_ALL = ~0
+};
+
+/* Input arguments */
+struct args {
+ const char* mesh;
+ int query_mask; /* Cmbination of query flag */
+};
+static const struct args ARGS_DEFAULT = {0};
+
+/* Command data */
+struct cmd {
+ struct args args;
+ struct smsh* mesh; /* Tetrahedral mesh */
+ uint64_t pagesize;
+};
+static const struct cmd CMD_NULL = {0};
+
+/*******************************************************************************
+ * Helper functions
+ ******************************************************************************/
+static INLINE void
+usage(FILE* stream)
+{
+ fprintf(stream, "usage: smsh-desc [-cdnpt] mesh\n");
+}
+
+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, "cdnpqt")) != -1) {
+ switch(opt) {
+ case 'c': args->query_mask |= QUERY_NCELLS; break;
+ case 'd': args->query_mask |= QUERY_DNODE; break;
+ case 'n': args->query_mask |= QUERY_NNODES; break;
+ case 'p': args->query_mask |= QUERY_PAGESIZE; break;
+ case 't': args->query_mask |= QUERY_DCELL; break;
+ default: res = RES_BAD_ARG;
+ }
+ if(res != RES_OK) goto error;
+ }
+
+ if(optind < argc) args->mesh = argv[optind];
+
+ /* By default, query all */
+ if(!args->query_mask) args->query_mask = QUERY_ALL;
+
+ if(!args->mesh) {
+ fprintf(stderr, "mesh is missing\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+exit:
+ return res;
+error:
+ usage(stderr);
+ 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;
+ FILE* fp = NULL;
+ res_T res = RES_OK;
+ ASSERT(cmd && args);
+
+ if(!(fp = fopen(args->mesh, "r"))) {
+ fprintf(stderr, "%s: %s\n", args->mesh, strerror(errno));
+ res = RES_IO_ERR;
+ goto error;
+ }
+ if(fread(&cmd->pagesize, sizeof(uint64_t), 1, fp) != 1) {
+ fprintf(stderr, "%s: %s\n", args->mesh, strerror(errno));
+ res = RES_IO_ERR;
+ goto error;
+ }
+
+ 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;
+
+exit:
+ if(fp) CHK(fclose(fp) == 0);
+ return res;
+error:
+ if(cmd->mesh) { SMSH(ref_put(cmd->mesh)); cmd->mesh = NULL; }
+ goto exit;
+}
+
+static void
+cmd_release(struct cmd* cmd)
+{
+ ASSERT(cmd);
+ if(cmd->mesh) SMSH(ref_put(cmd->mesh));
+}
+
+static res_T
+cmd_init(struct cmd* cmd, const struct args* args)
+{
+ res_T res = RES_OK;
+ ASSERT(cmd && args && args->query_mask);
+
+ if((res = setup_mesh(cmd, args)) != RES_OK) goto error;
+ cmd->args = *args;
+
+exit:
+ return res;
+error:
+ cmd_release(cmd);
+ goto exit;
+}
+
+static res_T
+cmd_run(struct cmd* cmd)
+{
+ struct smsh_desc desc = SMSH_DESC_NULL;
+ struct str str;
+ res_T res = RES_OK;
+ ASSERT(cmd);
+
+ str_init(NULL, &str);
+ SMSH(get_desc(cmd->mesh, &desc));
+
+ #define APPEND(Fmt, Val) { \
+ if((res = str_append_printf(&str, Fmt, (Val))) != RES_OK) goto error; \
+ } (void)0
+ if(cmd->args.query_mask & QUERY_PAGESIZE) APPEND("%zu ", cmd->pagesize);
+ if(cmd->args.query_mask & QUERY_NNODES) APPEND("%zu ", desc.nnodes);
+ if(cmd->args.query_mask & QUERY_NCELLS) APPEND("%zu ", desc.ncells);
+ if(cmd->args.query_mask & QUERY_DNODE) APPEND("%u ", desc.dnode);
+ if(cmd->args.query_mask & QUERY_DCELL) APPEND("%u ", desc.dcell);
+ APPEND("%s", cmd->args.mesh);
+ #undef APPEND
+
+ printf("%s\n", str_cget(&str));
+
+exit:
+ str_release(&str);
+ return res;
+error:
+ fprintf(stderr, "error: %s\n", res_to_cstr(res));
+ 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:
+ cmd_release(&cmd);
+ CHK(mem_allocated_size() == 0);
+ return err;
+error:
+ err = 1;
+ goto exit;
+}
diff --git a/src/smsh.c b/src/smsh.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2020-2023 |Méso|Star> (contact@meso-star.com)
+/* Copyright (C) 2020-2023, 2025 |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
diff --git a/src/smsh.h b/src/smsh.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2020-2023 |Méso|Star> (contact@meso-star.com)
+/* Copyright (C) 2020-2023, 2025 |Méso|Star> (contact@meso-star.com)
*
* This program is free software: you can redismshbute it and/or modify
* it under the terms of the GNU General Public License as published by
diff --git a/src/smsh2vtk.c b/src/smsh2vtk.c
@@ -0,0 +1,355 @@
+/* Copyright (C) 2020-2023, 2025 |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 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_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:
+ cmd_release(cmd);
+ goto exit;
+}
+
+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;
+}
diff --git a/src/smsh_c.h b/src/smsh_c.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2020-2023 |Méso|Star> (contact@meso-star.com)
+/* Copyright (C) 2020-2023, 2025 |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
diff --git a/src/smsh_log.c b/src/smsh_log.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2020-2023 |Méso|Star> (contact@meso-star.com)
+/* Copyright (C) 2020-2023, 2025 |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
diff --git a/src/smsh_log.h b/src/smsh_log.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2020-2023 |Méso|Star> (contact@meso-star.com)
+/* Copyright (C) 2020-2023, 2025 |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
diff --git a/src/test_smsh.c b/src/test_smsh.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2020-2023 |Méso|Star> (contact@meso-star.com)
+/* Copyright (C) 2020-2023, 2025 |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
diff --git a/src/test_smsh_load.c b/src/test_smsh_load.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2020-2023 |Méso|Star> (contact@meso-star.com)
+/* Copyright (C) 2020-2023, 2025 |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
diff --git a/src/vtk-data.c b/src/vtk-data.c
@@ -0,0 +1,284 @@
+/* Copyright (C) 2020-2023, 2025 |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 <rsys/cstr.h>
+#include <rsys/mem_allocator.h>
+#include <rsys/str.h>
+#include <rsys/text_reader.h>
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h> /* getopt */
+
+#define MAX_DATA 8
+
+/* Input arguments */
+struct args {
+ const char* output;
+ const char* data;
+ unsigned long nitems;
+ const char* names[MAX_DATA];
+ unsigned ndata;
+};
+static const struct args ARGS_DEFAULT = {0};
+
+/* Command data */
+struct cmd {
+ const char* names[MAX_DATA];
+ const char* data_name;
+ FILE* output;
+ FILE* data;
+ size_t nitems;
+};
+static const struct cmd CMD_NULL = {0};
+
+/*******************************************************************************
+ * Helper functions
+ ******************************************************************************/
+static INLINE void
+usage(FILE* stream)
+{
+ fprintf(stream,
+ "usage: vtk-data [-n name ...] [-o output] -c cell_count [data]\n");
+}
+
+static res_T
+parse_name(struct args* args, const char* name)
+{
+ unsigned i;
+ ASSERT(name);
+
+ if(args->ndata >= MAX_DATA) {
+ fprintf(stderr, "too many names\n");
+ return RES_BAD_ARG;
+ }
+
+ /* Names cannot contain white spaces */
+ if(strcspn(name, " \t") != strlen(name)) {
+ fprintf(stderr, "name \"%s\" cannot contain white spaces \n", name);
+ return RES_BAD_ARG;
+ }
+
+ /* Names must be unique */
+ FOR_EACH(i, 0, args->ndata) {
+ if(!strcmp(args->names[i], name)) {
+ fprintf(stderr, "name \"%s\" already defined\n", name);
+ return RES_BAD_ARG;
+ }
+ }
+
+ args->names[args->ndata++] = name;
+ return RES_OK;
+}
+
+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:c:n:")) != -1) {
+ switch(opt) {
+ case 'n': res = parse_name(args, optarg); break;
+ case 'o': args->output = optarg; break;
+ case 'c':
+ res = cstr_to_ulong(optarg, &args->nitems);
+ if(res == RES_OK && args->nitems == 0) res = RES_BAD_ARG;
+ break;
+ default: res = RES_BAD_ARG; goto error;
+ }
+ if(res != RES_OK) goto error;
+ }
+
+ if(optind < argc) args->data = argv[optind];
+
+ if(!args->nitems) {
+ fprintf(stderr, "missing item count -- option '-c'\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+exit:
+ return res;
+error:
+ usage(stderr);
+ goto exit;
+}
+
+static void
+cmd_release(struct cmd* cmd)
+{
+ if(cmd->data && cmd->data != stdin) CHK(!fclose(cmd->data));
+ if(cmd->output && cmd->output != stdout) CHK(!fclose(cmd->output));
+}
+
+static res_T
+cmd_init(struct cmd* cmd, const struct args* args)
+{
+ res_T res = RES_OK;
+ ASSERT(cmd && args);
+
+ if(!(cmd->output = args->output ? fopen(args->output, "w") : stdout)) {
+ perror(args->output);
+ res = RES_IO_ERR;
+ goto error;
+ }
+
+ if(!(cmd->data = args->data ? fopen(args->data, "r") : stdin)) {
+ perror(args->data);
+ res = RES_IO_ERR;
+ goto error;
+ }
+
+ cmd->data_name = args->data ? args->data : "stdin";
+ memcpy(cmd->names, args->names, sizeof(char*[MAX_DATA]));
+ cmd->nitems = args->nitems;
+
+exit:
+ return res;
+error:
+ cmd_release(cmd);
+ goto exit;
+}
+
+static res_T
+write_data(struct cmd* cmd, const int idata, struct txtrdr* txtrdr)
+{
+ size_t i = 0;
+ int res = RES_OK;
+
+ /* Pre-conditions */
+ ASSERT(cmd && idata < MAX_DATA && txtrdr);
+ ASSERT(txtrdr_get_line(txtrdr));
+
+ if(cmd->names[idata]) {
+ res = fprintf(cmd->output, "SCALARS %s double 1\n", cmd->names[idata]);
+ } else {
+ /* Make default name unique */
+ res = fprintf(cmd->output, "SCALARS data%d double 1\n", idata);
+ }
+ if(res < 0) {
+ perror(NULL);
+ res = RES_IO_ERR;
+ goto error;
+ }
+
+ if(fprintf(cmd->output, "LOOKUP_TABLE default\n") < 0) {
+ perror(NULL);
+ res = RES_IO_ERR;
+ goto error;
+ }
+
+ FOR_EACH(i, 0, cmd->nitems) {
+ const char* line = NULL;
+ double f = 0;
+
+ if(!(line = txtrdr_get_line(txtrdr))) {
+ fprintf(stderr, "missing data\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ if((sscanf(line, "%lf\n", &f)) != 1) {
+ fprintf(stderr, "%s:%lu: unable to read double\n",
+ txtrdr_get_name(txtrdr), txtrdr_get_line_num(txtrdr));
+ res = RES_IO_ERR;
+ goto error;
+ }
+
+ if(fprintf(cmd->output, "%lf\n", f) < 0) {
+ perror(NULL);
+ res = RES_IO_ERR;
+ goto error;
+ }
+
+ if((res = txtrdr_read_line(txtrdr)) != RES_OK) {
+ fprintf(stderr, "%s\n", res_to_cstr(res));
+ goto error;
+ }
+ }
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+static res_T
+cmd_run(struct cmd* cmd)
+{
+ struct txtrdr* txtrdr = NULL;
+ int i = 0;
+ res_T res = RES_OK;
+ ASSERT(cmd);
+
+ res = txtrdr_stream(NULL, cmd->data, cmd->data_name, '#', &txtrdr);
+ if(res != RES_OK) {
+ fprintf(stderr, "%s\n", res_to_cstr(res));
+ goto error;
+ }
+
+ if((res = txtrdr_read_line(txtrdr)) != RES_OK) {
+ fprintf(stderr, "%s\n", res_to_cstr(res));
+ goto error;
+ }
+
+ if(!txtrdr_get_line(txtrdr)) goto exit; /* No data */
+
+ if(fprintf(cmd->output, "CELL_DATA %zu\n", cmd->nitems) < 0) {
+ fprintf(stderr, "%s\n", strerror(errno));
+ res = RES_IO_ERR;
+ goto error;
+ }
+
+ FOR_EACH(i, 0, MAX_DATA) {
+ if((res = write_data(cmd, i, txtrdr)) != RES_OK) goto error;
+ if(!txtrdr_get_line(txtrdr)) break; /* No more data */
+ }
+
+exit:
+ if(txtrdr) txtrdr_ref_put(txtrdr);
+ return res;
+error:
+ goto exit;
+}
+
+/*******************************************************************************
+ * Main function
+ ******************************************************************************/
+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:
+ cmd_release(&cmd);
+ CHK(mem_allocated_size() == 0);
+ return err;
+error:
+ err = 1;
+ goto exit;
+}