star-cad

Geometric operators for computer-aided design
git clone git://git.meso-star.fr/star-cad.git
Log | Files | Refs | README | LICENSE

commit b032516cf6b787787637de02016ae648f6d05c05
parent 41a577dc8c1f2434bbc98524451c0358c5e0a0b5
Author: Christophe Coustet <christophe.coustet@meso-star.com>
Date:   Thu, 18 Jul 2024 11:13:01 +0200

Merge branch 'release_0.5'

Diffstat:
M.gitignore | 15++++++++-------
AMakefile | 182+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
MREADME.md | 89++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
Dcmake/CMakeLists.txt | 118-------------------------------------------------------------------------------
Aconfig.mk | 92+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Amake.sh | 70++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ascad.pc.in | 15+++++++++++++++
Msrc/scad.c | 142++++++++++++++++++++++++++++++++++++++++++-------------------------------------
Msrc/scad.h | 5+++--
Msrc/scad_c.h | 2+-
Msrc/scad_device.c | 2+-
Msrc/scad_device.h | 2+-
Msrc/scad_geometry.c | 6+++---
Msrc/scad_geometry.h | 2+-
Msrc/test_api.c | 14+++++---------
Msrc/test_common.h | 2+-
Msrc/test_export.c | 8++------
Msrc/test_lifetime.c | 3+--
Msrc/test_partition.c | 67++++++++++++++++++++++++++++++++++++++++++-------------------------
19 files changed, 555 insertions(+), 281 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -1,12 +1,13 @@ .gitignore -CMakeCache.txt -CMakeFiles -Makefile -tmp [Bb]uild* *.sw[po] -*.[ao] -*.orig +*.[aod] +*.so *~ +test* +!test*.[ch] +.config +.test tags -test +*.pc +*.stl diff --git a/Makefile b/Makefile @@ -0,0 +1,182 @@ +# Copyright (C) 2022-2024 |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/>. */ + +.POSIX: +.SUFFIXES: # Clean up default inference rules + +include config.mk + +LIBNAME_STATIC = libscad.a +LIBNAME_SHARED = libscad.so +LIBNAME = $(LIBNAME_$(LIB_TYPE)) + +################################################################################ +# Library building +################################################################################ +SRC =\ + src/scad.c\ + src/scad_device.c\ + src/scad_geometry.c +OBJ = $(SRC:.c=.o) +DEP = $(SRC:.c=.d) + +build_library: .config $(DEP) + @$(MAKE) -fMakefile $$(for i in $(DEP); do echo -f $${i}; done) \ + $$(if [ -n "$(LIBNAME)" ]; then\ + echo "$(LIBNAME)";\ + else\ + echo "$(LIBNAME_SHARED)";\ + fi) + +$(DEP) $(OBJ): config.mk + +$(LIBNAME_SHARED): $(OBJ) + $(CC) $(CFLAGS_SO) $(DPDC_CFLAGS) -o $@ $(OBJ) $(LDFLAGS_SO) $(DPDC_LIBS) + +$(LIBNAME_STATIC): libscad.o + $(AR) -rc $@ $? + $(RANLIB) $@ + +libscad.o: $(OBJ) + $(LD) -r $(OBJ) -o $@ + $(OBJCOPY) $(OCPFLAGS) $@ + +.config: config.mk + @if ! $(PKG_CONFIG) --atleast-version $(GMSH_VERSION) gmsh; then \ + echo "gmsh $(GMSH_VERSION) not found" >&2; exit 1; fi + @if ! $(PKG_CONFIG) --atleast-version $(RSYS_VERSION) rsys; then \ + echo "rsys $(RSYS_VERSION) not found" >&2; exit 1; fi + @if ! $(PKG_CONFIG) --atleast-version $(SENC3D_VERSION) senc3d; then \ + echo "senc3d $(SENC3D_VERSION) not found" >&2; exit 1; fi + @if ! $(PKG_CONFIG) --atleast-version $(SG3D_VERSION) sg3d; then \ + echo "sg3d $(SG3D_VERSION) not found" >&2; exit 1; fi + @echo "config done" > $@ + +.SUFFIXES: .c .d .o +.c.d: + @$(CC) $(CFLAGS_SO) $(DPDC_CFLAGS) -MM -MT "$(@:.d=.o) $@" $< -MF $@ + +.c.o: + $(CC) $(CFLAGS_SO) $(DPDC_CFLAGS) -DSCAD_SHARED_BUILD -c $< -o $@ + +################################################################################ +# Installation +################################################################################ +pkg: + sed -e 's#@PREFIX@#$(PREFIX)#g'\ + -e 's#@VERSION@#$(VERSION)#g'\ + -e 's#@GMSH_VERSION@#$(GMSH_VERSION)#g'\ + -e 's#@RSYS_VERSION@#$(RSYS_VERSION)#g'\ + -e 's#@SENC3D_VERSION@#$(SENC3D_VERSION)#g'\ + -e 's#@SG3D_VERSION@#$(SG3D_VERSION)#g'\ + scad.pc.in > scad.pc + +scad-local.pc: scad.pc.in + sed -e '1d'\ + -e 's#^includedir=.*#includedir=./src/#'\ + -e 's#^libdir=.*#libdir=./#'\ + -e 's#@VERSION@#$(VERSION)#g'\ + -e 's#@GMSH_VERSION@#$(GMSH_VERSION)#g'\ + -e 's#@RSYS_VERSION@#$(RSYS_VERSION)#g'\ + -e 's#@SENC3D_VERSION@#$(SENC3D_VERSION)#g'\ + -e 's#@SG3D_VERSION@#$(SG3D_VERSION)#g'\ + scad.pc.in > $@ + +install: build_library pkg + @$(SHELL) make.sh install "$(DESTDIR)$(PREFIX)/lib" $(LIBNAME) + @$(SHELL) make.sh install "$(DESTDIR)$(PREFIX)/lib/pkgconfig" scad.pc + @$(SHELL) make.sh install "$(DESTDIR)$(PREFIX)/include/star" src/scad.h + @$(SHELL) make.sh install "$(DESTDIR)$(PREFIX)/share/doc/star-cad" COPYING README.md + +uninstall: + rm -f "$(DESTDIR)$(PREFIX)/lib/$(LIBNAME)" + rm -f "$(DESTDIR)$(PREFIX)/lib/pkgconfig/scad.pc" + rm -f "$(DESTDIR)$(PREFIX)/include/star/scad.h" + rm -f "$(DESTDIR)$(PREFIX)/share/doc/star-cad/COPYING" + rm -f "$(DESTDIR)$(PREFIX)/share/doc/star-cad/README.md" + +################################################################################ +# Miscellaneous targets +################################################################################ +all: build_library build_tests + +clean: clean_test + rm -f $(OBJ) $(TEST_OBJ) $(LIBNAME) + rm -f .config .test libscad.o scad.pc scad-local.pc + +distclean: clean + rm -f $(DEP) $(TEST_DEP) + +lint: + shellcheck -o all make.sh + +################################################################################ +# Tests +################################################################################ +TEST_SRC =\ + src/test_api.c\ + src/test_export.c\ + src/test_lifetime.c\ + src/test_partition.c +TEST_OBJ = $(TEST_SRC:.c=.o) +TEST_DEP = $(TEST_SRC:.c=.d) + +PKG_CONFIG_LOCAL = PKG_CONFIG_PATH="./:$${PKG_CONFIG_PATH}" $(PKG_CONFIG) +SCAD_CFLAGS = $$($(PKG_CONFIG_LOCAL) $(PCFLAGS) --cflags scad-local.pc) +SCAD_LIBS = $$($(PKG_CONFIG_LOCAL) $(PCFLAGS) --libs scad-local.pc) + +build_tests: build_library $(TEST_DEP) .test + @$(MAKE) -fMakefile -f.test \ + $$(for i in $(TEST_DEP); do echo -f"$${i}"; done) \ + test_bin + +test: build_tests + @$(SHELL) make.sh run_test $(TEST_SRC) + +.test: Makefile + @$(SHELL) make.sh config_test $(TEST_SRC) > $@ + +clean_test: + $(SHELL) make.sh clean_test $(TEST_SRC) + rm -f bin_cube2.stl + rm -f bin_cube.stl + rm -f bin_rectangle.stl + rm -f cube2.stl + rm -f cube.stl + rm -f different_names_0.stl + rm -f not-a-volume.stl.stl + rm -f part_0.9_1o.stl + rm -f part_0.9_2o.stl + rm -f part_1.1_1.stl + rm -f part_1.1_2.stl + rm -f part_1_1.stl + rm -f part_1_2.stl + rm -f rectangle.stl + rm -f "sphere 1_0.stl" + rm -f "sphere 1.stl" + +$(TEST_DEP): config.mk scad-local.pc + @$(CC) $(CFLAGS_EXE) $(RSYS_CFLAGS) $(SCAD_CFLAGS) \ + -MM -MT "$(@:.d=.o) $@" $(@:.d=.c) -MF $@ + +$(TEST_OBJ): config.mk scad-local.pc + $(CC) $(CFLAGS_EXE) $(RSYS_CFLAGS) $(SCAD_CFLAGS) -c $(@:.o=.c) -o $@ + +test_api \ +test_export \ +test_lifetime \ +test_partition \ +: config.mk scad-local.pc $(LIBNAME) + $(CC) $(CFLAGS_EXE) -o $@ src/$@.o $(LDFLAGS_EXE) $(SCAD_LIBS) $(RSYS_LIBS) -lm diff --git a/README.md b/README.md @@ -1,33 +1,45 @@ -# Star-Cad +# Star CAD -Star-Cad is mostly a wrapper for the gmsh library. It only provides access to -some of the OpenCascade kernel features, with the benefit of simplified use. One -of the most valuable features of Star-Cad is that it implements an automated -reference counting mecanism that eases geometry reference, whilst gmsh system -of tags doesn't ensure tag persistence accross geometry deletions. +Star-CAD is mostly a wrapper for the gmsh library. It only provides +access to some of the OpenCascade kernel features, with the benefit of +simplified use. One of the most valuable features of Star-CAD is that it +implements an automated reference counting mecanism that eases geometry +reference, whilst gmsh system of tags doesn't ensure tag persistence +accross geometry deletions. -## How to build +## Requirements -The library uses [CMake](http://www.cmake.org) and the -[RCMake](https://gitlab.com/vaplv/rcmake/#tab-readme) package to build. It also -depends on the -[Gmsh](https://gmsh.info/), -[RSys](https://gitlab.com/vaplv/rsys/#tab-readme), -[star-geometry-3d](https://gitlab.com/meso-star/star-geometry-3d) and -[star-enclosures-3d](https://gitlab.com/meso-star/star-enclosures-3d) libraries. +- C compiler +- POSIX make +- pkg-config +- [Gmsh](https://gmsh.info/) +- [RSys](https://gitlab.com/vaplv/rsys) +- [Star Geometry 3D](https://gitlab.com/meso-star/star-geometry-3d) +- [Star Enclosures 3D](https://gitlab.com/meso-star/star-enclosures-3d) -First ensure that CMake is installed on your system. Then install the RCMake -package as well as all the aforementioned prerequisites. Then generate the -project from the `cmake/CMakeLists.txt` file by appending to the -`CMAKE_PREFIX_PATH` variable the install directories of its dependencies. +## Installation + +Edit config.mk as needed, then run: + + make clean install ## Release notes +### Version 0.5 + +- BugFix STL output of geometries when forcing normals +- Improve C89 compliance and fix header file to be used in c++ code +- Change build mechanics from cmake to POSIX makefiles +- Upgrade dependencies +- Improve and fix some tests + ### Version 0.4.1 -- Add scad_dump_geometry() API call that can be called from a debugging session +- Add `scad_dump_geometry` API call that can be called from a debugging + session - Remove an invalid assert -- Fix build that used gmsh library in Release when Debug should have been used +- Fix build that used gmsh library in Release when Debug should have + been used - Improve logs ### Version 0.4 @@ -44,26 +56,28 @@ project from the `cmake/CMakeLists.txt` file by appending to the This release largely breaks the API. - - Add API calls to attach custom data to geometries - - Increase default min size for generated meshes - - Add API calls to deals with the entities hidden in geometries - - Change geometry swap API - - Remove redundant API calls - - BugFixes on geometries lifetime management - - Log messages improvement - - Many bug fixes +- Add API calls to attach custom data to geometries +- Increase default min size for generated meshes +- Add API calls to deals with the entities hidden in geometries +- Change geometry swap API +- Remove redundant API calls +- BugFixes on geometries lifetime management +- Log messages improvement +- Many bug fixes ### Version 0.2 - Introduce reference counting for geometries (API break). -- Add the scad_stl_data_write API call to write a vector of coordinate as an STL - file the same way scad_stl_export do (producing the same decimals/bytes). -- Add scad_stl_get_data and scad_stl_get_data_partial API calls to get the mesh - of a geometry, allowing to manipulate it before writing it. -- Add the scad_stl_rxport_partial API call to export part of a geometry to an - STL file. +- Add the `scad_stl_data_write` API call to write a vector of coordinate + as an STL file the same way `scad_stl_export` do (producing the same + decimals/bytes). +- Add `scad_stl_get_data` and `scad_stl_get_data_partial` API calls to + get the mesh of a geometry, allowing to manipulate it before writing + it. +- Add the `scad_stl_export_partial` API call to export part of a + geometry to an STL file. - Remove the reverse arg from STL related functions (API break). -- Add a scad_geometry_reverse API call. +- Add a `scad_geometry_reverse` API call. - Code simplifications in STL-related code ### Version 0.1.0 @@ -72,7 +86,8 @@ Initial version. ## License -Copyright (C) 2022-2023, 2021 |Meso|Star> (<contact@meso-star.com>). -Star-Cad is free software released under the GPL v3+ license: GNU GPL +Copyright (C) 2022-2024 |Méso|Star> (contact@meso-star.com) + +Star-CAD is free software released under the GPL v3+ license: GNU GPL version 3 or later. You are welcome to redistribute it under certain conditions; refer to the COPYING file for details. diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -1,118 +0,0 @@ -# Copyright (C) 2022 |Meso|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/>. - -cmake_minimum_required(VERSION 3.1) -project(star-cad C) -enable_testing() - -set(SCAD_SOURCE_DIR ${PROJECT_SOURCE_DIR}/../src) -option(NO_TEST "Disable the test" OFF) - -################################################################################ -# Check dependencies -################################################################################ -find_package(gmsh 4.12.2 REQUIRED) -find_package(RCMake 0.4.1 REQUIRED) -find_package(RSys 0.12.1 REQUIRED) -find_package(StarGeom3D 0.1.3 REQUIRED) -find_package(StarEnc3D 0.6 REQUIRED) - -set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${RCMAKE_SOURCE_DIR}) -include(rcmake) -include(rcmake_runtime) - -include_directories( - ${GMSH_INCLUDE_DIR} - ${RSys_INCLUDE_DIR} - ${StarGeom3D_INCLUDE_DIR} - ${StarEnc3D_INCLUDE_DIR}) - -################################################################################ -# Configure and define targets -################################################################################ -set(VERSION_MAJOR 0) -set(VERSION_MINOR 4) -set(VERSION_PATCH 1) -set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}) - -set(SCAD_FILES_SRC - scad.c - scad_device.c - scad_geometry.c) -set(SCAD_FILES_INC_API scad.h) -set(SCAD_FILES_INC - scad_device.h - scad_geometry.h) -set(SCAD_FILES_DOC COPYING README.md) - -# Prepend each file in the `SCAD_FILES_<SRC|INC>' list by `SCAD_SOURCE_DIR' -rcmake_prepend_path(SCAD_FILES_SRC ${SCAD_SOURCE_DIR}) -rcmake_prepend_path(SCAD_FILES_INC ${SCAD_SOURCE_DIR}) -rcmake_prepend_path(SCAD_FILES_INC_API ${SCAD_SOURCE_DIR}) -rcmake_prepend_path(SCAD_FILES_DOC ${PROJECT_SOURCE_DIR}/../) - -add_library(scad SHARED ${SCAD_FILES_SRC} ${SCAD_FILES_INC} ${SCAD_FILES_INC_API}) -target_link_libraries(scad RSys gmsh StarGeom3D StarEnc3D m) - -set_target_properties(scad PROPERTIES - DEFINE_SYMBOL SCAD_SHARED_BUILD - VERSION ${VERSION} - SOVERSION ${VERSION_MAJOR}) - -rcmake_setup_devel(scad StarCAD ${VERSION} star/scad_version.h) - -################################################################################ -# Add tests -################################################################################ -if(NOT NO_TEST) - function(build_test _name) - add_executable(${_name} - ${SCAD_SOURCE_DIR}/${_name}.c - ${SCAD_SOURCE_DIR}/test_common.h) - target_link_libraries(${_name} scad RSys) - set(_libraries ${ARGN}) - foreach(_lib ${_libraries}) - target_link_libraries(${_name} ${_lib}) - endforeach() - endfunction() - - function(register_test _name) - add_test(${_name} ${ARGN}) - endfunction() - - function(new_test _name) - build_test(${_name} ${ARGN}) - register_test(${_name} ${_name}) - endfunction() - - new_test(test_api) - new_test(test_export) - new_test(test_partition) - new_test(test_lifetime) - - rcmake_copy_runtime_libraries(test) - -endif(NOT NO_TEST) - -################################################################################ -# Define output & install directories -################################################################################ -install(TARGETS scad - ARCHIVE DESTINATION bin - LIBRARY DESTINATION lib - RUNTIME DESTINATION bin) -install(FILES ${SCAD_FILES_INC_API} DESTINATION include/star) -install(FILES ${SCAD_FILES_DOC} DESTINATION share/doc/star-cad) - diff --git a/config.mk b/config.mk @@ -0,0 +1,92 @@ +VERSION = 0.5.0 +PREFIX = /usr/local + +LIB_TYPE = SHARED +#LIB_TYPE = STATIC + +BUILD_TYPE = RELEASE +#BUILD_TYPE = DEBUG + +################################################################################ +# Tools +################################################################################ +AR = ar +CC = cc +LD = ld +OBJCOPY = objcopy +PKG_CONFIG = pkg-config +RANLIB = ranlib + +################################################################################ +# Dependencies +################################################################################ +PCFLAGS_STATIC = --static +PCFLAGS = $(PCFLAGS_$(LIB_TYPE)) + +GMSH_VERSION = 4.12.2 +GMSH_CFLAGS = $$($(PKG_CONFIG) $(PCFLAGS) --cflags gmsh) +GMSH_LIBS = $$($(PKG_CONFIG) $(PCFLAGS) --libs gmsh) + +RSYS_VERSION = 0.14 +RSYS_CFLAGS = $$($(PKG_CONFIG) $(PCFLAGS) --cflags rsys) +RSYS_LIBS = $$($(PKG_CONFIG) $(PCFLAGS) --libs rsys) + +SENC3D_VERSION = 0.7.1 +SENC3D_CFLAGS = $$($(PKG_CONFIG) $(PCFLAGS) --cflags senc3d) +SENC3D_LIBS = $$($(PKG_CONFIG) $(PCFLAGS) --libs senc3d) + +SG3D_VERSION = 0.2 +SG3D_CFLAGS = $$($(PKG_CONFIG) $(PCFLAGS) --cflags sg3d) +SG3D_LIBS = $$($(PKG_CONFIG) $(PCFLAGS) --libs sg3d) + +DPDC_CFLAGS = $(GMSH_CFLAGS) $(RSYS_CFLAGS) $(SENC3D_CFLAGS) $(SG3D_CFLAGS) +DPDC_LIBS = $(GMSH_LIBS) $(RSYS_LIBS) $(SENC3D_LIBS) $(SG3D_LIBS) -lm + +################################################################################ +# Compilation options +################################################################################ +WFLAGS =\ + -Wall\ + -Wcast-align\ + -Wconversion\ + -Wextra\ + -Wmissing-declarations\ + -Wmissing-prototypes\ + -Wshadow + +CFLAGS_HARDENED =\ + -D_FORTIFY_SOURCES=2\ + -fcf-protection=full\ + -fstack-clash-protection\ + -fstack-protector-strong + +CFLAGS_COMMON =\ + -std=c89\ + -pedantic\ + -fPIC\ + -fvisibility=hidden\ + -fstrict-aliasing\ + $(CFLAGS_HARDENED)\ + $(WFLAGS) + +CFLAGS_DEBUG = -g $(CFLAGS_COMMON) +CFLAGS_RELEASE = -O2 -DNDEBUG $(CFLAGS_COMMON) +CFLAGS = $(CFLAGS_$(BUILD_TYPE)) + +CFLAGS_SO = $(CFLAGS) -fPIC +CFLAGS_EXE = $(CFLAGS) -fPIE + +################################################################################ +# Linker options +################################################################################ +LDFLAGS_HARDENED = -Wl,-z,relro,-z,now +LDFLAGS_DEBUG = $(LDFLAGS_HARDENED) +LDFLAGS_RELEASE = -s $(LDFLAGS_HARDENED) +LDFLAGS = $(LDFLAGS_$(BUILD_TYPE)) + +LDFLAGS_SO = $(LDFLAGS) -shared -Wl,--no-undefined +LDFLAGS_EXE = $(LDFLAGS) -pie + +OCPFLAGS_DEBUG = --localize-hidden +OCPFLAGS_RELEASE = --localize-hidden --strip-unneeded +OCPFLAGS = $(OCPFLAGS_$(BUILD_TYPE)) diff --git a/make.sh b/make.sh @@ -0,0 +1,70 @@ +#!/bin/sh + +# Copyright (C) 2022-2024 |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/scad.pc.in b/scad.pc.in @@ -0,0 +1,15 @@ +prefix=@PREFIX@ +includedir=${prefix}/include +libdir=${prefix}/lib + +Requires: rsys >= @RSYS_VERSION@ +Requires.private: \ + gmsh >= @GMSH_VERSION@, \ + senc3d >= @SENC3D_VERSION@, \ + sg3d >= @SG3D_VERSION@ +Name: Star-CAD +Description: Star CAD +Version: @VERSION@ +Libs: -L${libdir} -lscad +Libs.private: -lm +CFlags: -I${includedir} diff --git a/src/scad.c b/src/scad.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2022 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2022-2024 |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 @@ -25,6 +25,7 @@ #include <rsys/str.h> #include <rsys/dynamic_array_int.h> #include <rsys/dynamic_array_double.h> +#include <rsys/dynamic_array_char.h> #include <rsys/double3.h> #include <rsys/float3.h> @@ -90,11 +91,11 @@ write_ascii_stl } OKP(fprintf(stl_file, "endsolid \n")); if(actual_trg_count != count) { - long long unsigned del_count = count - actual_trg_count; + size_t del_count = count - actual_trg_count; ASSERT(actual_trg_count < count); log_warning(get_device(), - "In file '%s': %llu nul triangle(s) removed in exported geometry.\n", - filename, del_count); + "In file '%s': %lu nul triangle(s) removed in exported geometry.\n", + filename, (unsigned long)del_count); } exit: @@ -479,7 +480,7 @@ scad_stl_sort_orientation struct logger* logger = dev->logger; double* coord; size_t coord_n; - unsigned i, ecount, tcount_in, vcount_in, tcount, vcount; + unsigned i, ecount, tcount_in, tcount_out, vcount_in, utcount_in, vcount; struct sg3d_device* sg3d = NULL; struct sg3d_geometry* geom = NULL; struct senc3d_device* senc3d = NULL; @@ -489,8 +490,9 @@ scad_stl_sort_orientation int convention, initialized = 0; struct sg3d_geometry_add_callbacks callbacks = SG3D_ADD_CALLBACKS_NULL__; struct darray_double new_triangles; - char *process = NULL, *contact_0 = NULL; - unsigned ocount = 0, process_count = 0; + char* tin = NULL; + char *contact_0 = NULL; + unsigned ocount = 0; if(!triangles || !set_name) { res = RES_BAD_ARG; @@ -520,12 +522,12 @@ scad_stl_sort_orientation ERR(sg3d_geometry_add(geom, vcount_in, tcount_in, &callbacks, coord)); ERR(sg3d_geometry_get_unique_vertices_count(geom, &vcount)); - ERR(sg3d_geometry_get_unique_triangles_count(geom, &tcount)); - if(tcount != tcount_in) { - ASSERT(tcount_in > tcount); + ERR(sg3d_geometry_get_unique_triangles_count(geom, &utcount_in)); + if(utcount_in != tcount_in) { + ASSERT(tcount_in > utcount_in); log_warning(get_device(), "Triangles duplicates found when sorting out normals (%u / %u) in set '%s'.\n", - tcount_in - tcount, tcount_in, set_name); + tcount_in - utcount_in, tcount_in, set_name); } if(orientation == Scad_force_normals_outward) convention = SENC3D_CONVENTION_NORMAL_BACK | SENC3D_CONVENTION_NORMAL_OUTSIDE; @@ -536,7 +538,7 @@ scad_stl_sort_orientation dev->options.Geometry.OCCParallel ? SENC3D_NTHREADS_DEFAULT : 1, dev->verbose, &senc3d)); ERR(senc3d_scene_create(senc3d, convention, - tcount, sg3d_sencXd_geometry_get_indices, NULL, + utcount_in, sg3d_sencXd_geometry_get_indices, NULL, vcount, sg3d_sencXd_geometry_get_position, geom, &senc3d_scn)); ERR(senc3d_scene_get_overlapping_triangles_count(senc3d_scn, &ocount)); @@ -544,12 +546,16 @@ scad_stl_sort_orientation logger_print(logger, LOG_ERROR, "Overlapping triangles found when sorting out normals (%u / %u) " "in set '%s'.\n", - tcount_in - ocount, tcount_in, set_name); + utcount_in - ocount, utcount_in, set_name); res = RES_BAD_ARG; goto error; } ERR(senc3d_scene_get_enclosure_count(senc3d_scn, &ecount)); + /* If there is at least 1 triangle, there is at least enclosure #0 that + * represents the outside of the whole scene. + * For the model to allow forcing normals, we need a model defining an + * inside/outside frontier. These models have at least 2 enclosures. */ if(ecount < 2) { /* Must define a closed volume */ log_error(get_device(), @@ -557,49 +563,35 @@ scad_stl_sort_orientation res = RES_BAD_ARG; goto error; } else { - /* Enclosure 0 is allways present and represents the outside of the whole - * scene. Enclosures with rank 1+ are closed enclosures: normal orientation - * makes sense there. - * We will only process triangles that limit the enclosures that have - * enclosure 0 on the other side. If we processed any enclosure, possible - * internal enclosures would trigger a double processing of some triangles. - * Finally, no enclosure is allowed other than 1) enclosures in contact with - * enclosure 0, or 2) enclosures in contact with enclosures in 1). */ + /* For the model to allow forcing normals, we reject models defining + * enclosures included inside other enclosures and models with triangles + * that have their 2 sides in the output, as their normal's orientation + * could not satisfy the requirement of the 2 sides. + * To properly detect enclosures inside enclosures, we rely on the number of + * output triangles, that must be identical to the number of input + * triangles. */ unsigned e, enclosures[2]; + size_t two = 0; darray_double_init(allocator, &new_triangles); initialized = 1; - process = MEM_CALLOC(allocator, ecount, sizeof(*process)); contact_0 = MEM_CALLOC(allocator, ecount, sizeof(*contact_0)); - if(!process || !contact_0) { + if(!contact_0) { res = RES_MEM_ERR; goto error; } - for(e = 0; e < ecount; e++) { + /* Triangles of enclosure #0 are also part of other enclosures. To have them + * with the expected normal orientation we have to get them from these other + * enclosures. */ + for(e = 1; e < ecount; e++) { ERR(senc3d_scene_get_enclosure(senc3d_scn, e, &enclosure)); ERR(senc3d_enclosure_get_header(enclosure, &header)); - if(header.primitives_count != header.unique_primitives_count) { - log_error(get_device(), - "Triangle set '%s' define an invalid closed volume " - "(both sides of %u / %u triangles are in).\n", - set_name, - header.primitives_count - header.unique_primitives_count, - header.unique_primitives_count); - res = RES_BAD_ARG; - goto error; - } - if(e == 0) { - ERR(senc3d_enclosure_ref_put(enclosure)); - enclosure = NULL; - continue; - } + two += (header.primitives_count - header.unique_primitives_count); for(i = 0; i < header.unique_primitives_count; i++) { enum senc3d_side side; unsigned gid; ERR(senc3d_enclosure_get_triangle_id(enclosure, i, &gid, &side)); ERR(senc3d_scene_get_triangle_enclosures(senc3d_scn, gid, enclosures)); if(enclosures[0] == 0 || enclosures[1] == 0) { - process_count++; - process[e] = 1; contact_0[e] = 1; break; } @@ -607,38 +599,44 @@ scad_stl_sort_orientation ERR(senc3d_enclosure_ref_put(enclosure)); enclosure = NULL; } - for(e = 1; e < ecount; e++) { - if(process[e]) continue; - ERR(senc3d_scene_get_enclosure(senc3d_scn, e, &enclosure)); - ERR(senc3d_enclosure_get_header(enclosure, &header)); - for(i = 0; i < header.unique_primitives_count; i++) { - enum senc3d_side side; - unsigned gid; - ERR(senc3d_enclosure_get_triangle_id(enclosure, i, &gid, &side)); - ERR(senc3d_scene_get_triangle_enclosures(senc3d_scn, gid, enclosures)); - if(contact_0[enclosures[0]] || contact_0[enclosures[1]]) { - process_count++; - process[e] = 1; - break; - } - } - ERR(senc3d_enclosure_ref_put(enclosure)); - enclosure = NULL; - } - if(process_count != ecount -1) { + if(two > 0) { log_error(get_device(), - "Triangle set '%s' defines a topology that doesn't allow forcing normals.\n", - set_name); + "Triangle set '%s' define an invalid closed volume " + "(%u / %u triangles with both sides in).\n", + set_name, (unsigned)two, utcount_in); res = RES_BAD_ARG; goto error; } - /* Just a guess */ - ERR(darray_double_reserve(&new_triangles, 9 * tcount_in)); + ERR(darray_double_reserve(&new_triangles, 9 * utcount_in)); + tin = MEM_CALLOC(allocator, 1, 9 * utcount_in); + if(!tin) { + res= RES_MEM_ERR; + goto error; + } for(e = 1; e < ecount; e++) { + if(!contact_0[e]) { + /* Output only enclosures in contact with enclosure #0 */ + continue; + } ERR(senc3d_scene_get_enclosure(senc3d_scn, e, &enclosure)); ERR(senc3d_enclosure_get_header(enclosure, &header)); for(i = 0; i < header.unique_primitives_count; i++) { - unsigned n, k, vrt[3]; + unsigned n, k, idx, vrt[3]; + enum senc3d_side side; + /* Ensure that input triangles are included only once */ + ERR(senc3d_enclosure_get_triangle_id(enclosure, i, &idx, &side)); + if(tin[idx]) { + /* Allready in output */ + char s = (side == SENC3D_FRONT) ? 1 : 2;; + if(s == tin[idx]) continue; /* Same side => OK (should not happen?) */ + log_error(get_device(), + "Triangle set '%s' defines a topology that doesn't allow forcing normals" + " (found triangle(s) with 2 sides in).\n", + set_name); + res = RES_BAD_ARG; + goto error; + } + tin[idx] = (side == SENC3D_FRONT) ? 1 : 2;; /* Get the vertices as they are ordered in the enclosure's mesh */ ERR(senc3d_enclosure_get_triangle(enclosure, i, vrt)); /* Rewrite vertices according to enclosure's mesh */ @@ -653,13 +651,23 @@ scad_stl_sort_orientation ERR(senc3d_enclosure_ref_put(enclosure)); enclosure = NULL; } + tcount_out = (unsigned)(darray_double_size_get(&new_triangles)/9); + ASSERT(utcount_in >= tcount_out); + if(utcount_in != tcount_out) { + log_error(get_device(), + "Triangle set '%s' defines a topology that doesn't allow forcing normals" + " (%u /%u triangles not part of the output).\n", + set_name, utcount_in - tcount_out, utcount_in); + res = RES_BAD_ARG; + goto error; + } ERR(darray_double_copy_and_release(triangles, &new_triangles)); initialized = 0; } exit: - MEM_RM(allocator, process); MEM_RM(allocator, contact_0); + MEM_RM(allocator, tin); if(initialized) darray_double_release(&new_triangles); if(sg3d) SG3D(device_ref_put(sg3d)); if(geom) SG3D(geometry_ref_put(geom)); @@ -704,7 +712,7 @@ scad_stl_data_write /* If sort_orientation fails, try to write the file anyway to allow debugging */ tmp_res = scad_stl_sort_orientation(&sorted, filename, orientation); coord = darray_double_data_get(&sorted); - coord_n = darray_double_size_get(triangles); + coord_n = darray_double_size_get(&sorted); if(binary) ERR(write_binary_stl(filename, coord, coord_n/9)); else ERR(write_ascii_stl(filename, coord, coord_n/9)); ERR(tmp_res); diff --git a/src/scad.h b/src/scad.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2022 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2022-2024 |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 @@ -549,6 +549,7 @@ scad_geometry_normal /* Open gmsh in GUI mode so that the model can be inspected and even modified. * To use it from gdb: + * Requires a gmsh library (either release or debug version) with FLTK enabled. * (gdb) call scad_run_ui() * (then close gmsh and gdb is back to the breakpoint). */ SCAD_API res_T @@ -584,7 +585,6 @@ scad_get_dimtag_refcount SCAD_API res_T scad_dump_geometry (const struct scad_geometry* geom); -END_DECLS /* Dump all the geometries with address/name, ref count and tags. * To use it from gdb: @@ -593,6 +593,7 @@ END_DECLS SCAD_API void scad_dump_geometries (void); + END_DECLS #endif /* SCAD_H */ diff --git a/src/scad_c.h b/src/scad_c.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2022 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2022-2024 |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/scad_device.c b/src/scad_device.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2022 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2022-2024 |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/scad_device.h b/src/scad_device.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2022 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2022-2024 |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/scad_geometry.c b/src/scad_geometry.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2022 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2022-2024 |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 @@ -123,7 +123,7 @@ mixed_dim_err_msg else if (*dim != d) { log_error(dev, "Dimension mismatch in %s operation creating '%s' geometry.\n", - op, (name ? name : "unamed")); + op, (name ? name : "unnamed")); return 1; } } @@ -1347,7 +1347,7 @@ scad_geometries_common_boundaries if(name) { log_message(dev, "Common boundary '%s' is empty.\n", name); } else { - log_message(dev, "Unamed common boundary %p is empty.\n", (void*)geom); + log_message(dev, "Unnamed common boundary %p is empty.\n", (void*)geom); } geom->gmsh_dimTags = NULL; } else { diff --git a/src/scad_geometry.h b/src/scad_geometry.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2022 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2022-2024 |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_api.c b/src/test_api.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2022 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2022-2024 |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 @@ -21,7 +21,6 @@ #include <rsys/str.h> #include <rsys/math.h> #include <rsys/mem_allocator.h> -#include <rsys/logger.h> #include <rsys/dynamic_array_double.h> #include <stdlib.h> @@ -54,7 +53,6 @@ main(int argc, char* argv[]) struct scad_geometry* geoms[2]; struct scad_geometry* out_geoms[2]; struct mem_allocator allocator; - struct logger logger; struct darray_double trg; struct scad_options options = SCAD_DEFAULT_OPTIONS; const char* name; @@ -63,7 +61,6 @@ main(int argc, char* argv[]) (void)argc; (void)argv; OK(mem_init_proxy_allocator(&allocator, &mem_default_allocator)); - OK(logger_init(&allocator, &logger)); /* cannot call any API function before a successful call to scad_initialize */ BAD(scad_finalize()); @@ -82,7 +79,7 @@ main(int argc, char* argv[]) BAD(scad_geometry_get_name(geom, &name)); /* cannot call any API function after a successful call to scad_finalize */ - OK(scad_initialize(&logger, &allocator, 3)); + OK(scad_initialize(NULL, &allocator, 3)); OK(scad_finalize()); BAD(scad_finalize()); @@ -100,10 +97,10 @@ main(int argc, char* argv[]) BAD(scad_run_ui()); BAD(scad_geometry_get_name(geom, &name)); - BAD(scad_initialize(&logger, &allocator, 4)); - BAD(scad_initialize(&logger, &allocator, -1)); + BAD(scad_initialize(NULL, &allocator, 4)); + BAD(scad_initialize(NULL, &allocator, -1)); - OK(scad_initialize(&logger, &allocator, 3)); + OK(scad_initialize(NULL, &allocator, 3)); OK(scad_set_options(NULL)); OK(scad_set_options(&options)); @@ -296,7 +293,6 @@ main(int argc, char* argv[]) OK(scad_geometry_get_name(geom2, &name)); OK(scad_finalize()); - logger_release(&logger); check_memory_allocator(&allocator); mem_shutdown_proxy_allocator(&allocator); diff --git a/src/test_common.h b/src/test_common.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2022 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2022-2024 |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_export.c b/src/test_export.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2022 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2022-2024 |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 @@ -20,7 +20,6 @@ #include <rsys/rsys.h> #include <rsys/math.h> #include <rsys/mem_allocator.h> -#include <rsys/logger.h> #include <stdlib.h> @@ -36,13 +35,11 @@ main(int argc, char* argv[]) struct scad_geometry* cube2 = NULL; struct scad_geometry* geoms[2]; struct mem_allocator allocator; - struct logger logger; (void)argc; (void)argv; OK(mem_init_proxy_allocator(&allocator, &mem_default_allocator)); - OK(logger_init(&allocator, &logger)); - OK(scad_initialize(&logger, &allocator, 3)); + OK(scad_initialize(NULL, &allocator, 3)); OK(scad_add_rectangle("rectangle", p1, d1, &rectangle)); OK(scad_add_box("cube", p1, d1, &cube)); @@ -67,7 +64,6 @@ main(int argc, char* argv[]) OK(scad_finalize()); - logger_release(&logger); check_memory_allocator(&allocator); mem_shutdown_proxy_allocator(&allocator); CHK(mem_allocated_size() == 0); diff --git a/src/test_lifetime.c b/src/test_lifetime.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2022 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2022-2024 |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 @@ -21,7 +21,6 @@ #include <rsys/str.h> #include <rsys/math.h> #include <rsys/mem_allocator.h> -#include <rsys/logger.h> #include <rsys/dynamic_array_double.h> #include <stdlib.h> diff --git a/src/test_partition.c b/src/test_partition.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2022 |Meso|Star> (contact@meso-star.com) +/* Copyright (C) 2022-2024 |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 @@ -22,52 +22,71 @@ #include <rsys/rsys.h> #include <rsys/math.h> #include <rsys/mem_allocator.h> -#include <rsys/logger.h> #include <rsys/double3.h> #include <stdlib.h> #include <stdio.h> +/* + * +-------+ +-------+ +------+ + * | | +----+ | +----+ | +----+ + * | | | | | | | | || | + * | | +----+ | +----+ | +----+ + * +-------+ +-------+ +------+ + * x = 1.1 x = 1 x = 0.9 + */ + static res_T two_geoms (double x, const int allow_overlapping, - struct mem_allocator* allocator, - struct logger* logger) + struct mem_allocator* allocator) { - double p0[3] = {0, 0, 2}; - double d0[3] = {1, 1, 1}; double p1[3] = {0, 0, 0}; double d1[3] = {1, 1, 1}; double p2[3]; double d2[3] = {0.5, 0.5, 0.5}; struct scad_geometry* geoms[2]; - struct scad_geometry* tmp[2]; struct scad_geometry* out_geoms[2]; char name[64]; res_T res; - OK(scad_initialize(logger, allocator, 3)); + OK(scad_initialize(NULL, allocator, 3)); /* set position for cube #2 */ d3(p2, x, 0.25, 0.25); - OK(scad_add_box("cube0", p0, d0, tmp)); - OK(scad_add_box("cube1", p1, d1, tmp+1)); - - OK(scad_fuse_geometries("cubes", tmp, 1, tmp+1, 1, geoms)); + OK(scad_add_box("cube1", p1, d1, geoms+0)); OK(scad_add_box("cube2", p2, d2, geoms+1)); + /* Try to partition. + * Fails if the 2 cubes overlap and overlapping is not allowed. */ res = scad_geometries_partition(geoms, 2, allow_overlapping, out_geoms); if(res != RES_OK) goto end; + /* Try to export the partitioned geometry in STL files. + * If normal are forced, it fails when the geometry does not properly define + * an inside / outside. */ OK(scad_scene_mesh()); if(allow_overlapping) { snprintf(name, sizeof(name), "part_%g_1o", x); - OK(scad_stl_export(out_geoms[0], name, Scad_force_normals_outward, 0)); + /* If x==0.9, cubes 1 & 2 intersect. As a consequence, the partitioned + * geometry of both cubes does not define and inside/outside and the + * partitioned geometry cannot have its normals forced */ + if(x == 0.9) { + BAD(scad_stl_export(out_geoms[0], name, Scad_force_normals_outward, 0)); + OK(scad_stl_export(out_geoms[0], name, Scad_keep_normals_unchanged, 0)); + } else { + OK(scad_stl_export(out_geoms[0], name, Scad_force_normals_outward, 1)); + } snprintf(name, sizeof(name), "part_%g_2o", x); - OK(scad_stl_export(out_geoms[1], name, Scad_force_normals_outward, 1)); + if(x == 0.9) { + BAD(scad_stl_export(out_geoms[1], name, Scad_force_normals_outward, 1)); + OK(scad_stl_export(out_geoms[1], name, Scad_keep_normals_unchanged, 1)); + } else { + OK(scad_stl_export(out_geoms[1], name, Scad_force_normals_outward, 1)); + } } else { snprintf(name, sizeof(name), "part_%g_1", x); OK(scad_stl_export(geoms[0], name, Scad_force_normals_outward, 0)); @@ -84,25 +103,23 @@ int main(int argc, char* argv[]) { struct mem_allocator allocator; - struct logger logger; res_T res = RES_OK; (void)argc; (void)argv; OK(mem_init_proxy_allocator(&allocator, &mem_default_allocator)); - OK(logger_init(&allocator, &logger)); - - /* First with distant geometries */ - OK(two_geoms(1.1, 0, &allocator, &logger)); - /* First with contacting geometries */ - OK(two_geoms(1, 0, &allocator, &logger)); + /* With distant geometries */ + OK(two_geoms(1.1, 0, &allocator)); + OK(two_geoms(1.1, 1, &allocator)); - /* First with overlapping geometries */ - BAD(two_geoms(0.9, 0, &allocator, &logger)); - OK(two_geoms(0.9, 1, &allocator, &logger)); + /* With contacting geometries */ + OK(two_geoms(1, 0, &allocator)); + OK(two_geoms(1, 1, &allocator)); - logger_release(&logger); + /* With overlapping geometries */ + BAD(two_geoms(0.9, 0, &allocator)); + OK(two_geoms(0.9, 1, &allocator)); check_memory_allocator(&allocator); mem_shutdown_proxy_allocator(&allocator);