commit 3e66a85c6765bcea4401c206f1310e242f97271e
parent 73052ae91d67aed8e7a9681ec0f2b6798d56b1be
Author: Christophe Coustet <christophe.coustet@meso-star.com>
Date: Fri, 24 Mar 2023 14:48:17 +0100
Merge branch 'release_0.1'
Diffstat:
| A | .gitignore | | | 12 | ++++++++++++ |
| D | Makefile | | | 41 | ----------------------------------------- |
| M | README.md | | | 35 | +++++++++++++++++++++++++++++------ |
| A | cmake/CMakeLists.txt | | | 111 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| D | config.mk | | | 10 | ---------- |
| M | src/scad.c | | | 1059 | +++++++++++++++++++++++++++++-------------------------------------------------- |
| M | src/scad.h | | | 540 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------- |
| A | src/scad_c.h | | | 36 | ++++++++++++++++++++++++++++++++++++ |
| A | src/scad_device.c | | | 434 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | src/scad_device.h | | | 158 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | src/scad_geometry.c | | | 1960 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | src/scad_geometry.h | | | 37 | +++++++++++++++++++++++++++++++++++++ |
| D | src/test.c | | | 48 | ------------------------------------------------ |
| A | src/test_api.c | | | 265 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | src/test_common.h | | | 38 | ++++++++++++++++++++++++++++++++++++++ |
| A | src/test_export.c | | | 73 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | src/test_partition.c | | | 112 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
17 files changed, 4079 insertions(+), 890 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -0,0 +1,12 @@
+.gitignore
+CMakeCache.txt
+CMakeFiles
+Makefile
+tmp
+[Bb]uild*
+*.sw[po]
+*.[ao]
+*.orig
+*~
+tags
+test
diff --git a/Makefile b/Makefile
@@ -1,41 +0,0 @@
-.SUFFIXES: # Clean up default inference rules
-
-include config.mk
-
-SRC = src/scad.c
-
-OBJ = $(SRC:.c=.o)
-
-all: static_lib
-
-$(OBJ): config.mk
-
-static_lib: $(OBJ)
- $(AR) $(AFLAGS) libscad.a $(OBJ)
-
-.SUFFIXES: .c .o
-.c.o:
- $(CC) $(CFLAGS) -c $< -o $@
-
-#------------------------------------------------------------------------------
-
-test: $(OBJ) src/test.o
- $(CC) -o $@ src/test.o $(OBJ) $(LDLIBS) $(LDFLAGS)
-
-src/test.o: src/test.c
- $(CC) $(CFLAGS) -c $< -o $@
-
-clean:
- @rm -f $(OBJ) libscad.a src/test.o test
-
-API = src/scad.h
-
-install: static_lib
- mkdir -p $(PREFIX)/lib
- mkdir -p $(PREFIX)/include/
- cp libscad.a $(PREFIX)/lib
- cp $(API) $(PREFIX)/include/
-
-uninstall:
- rm -f $(PREFIX)/lib/libscad.a
- rm -f $(PREFIX)/include/scad.h
diff --git a/README.md b/README.md
@@ -1,10 +1,33 @@
# Star-Cad
-Star-Cad is a gmsh wrapper library.
+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 depends on :
-- gmsh https://gmsh.info/
-- Rsys https://gitlab.com/vaplv/rsys
+## How to build
-To build Star-Cad modify the config.mk to find Rsys and Gmsh on your system.
-Star-Cad is build as a static library.
+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/) and
+[RSys](https://gitlab.com/vaplv/rsys/#tab-readme) libraries.
+
+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.
+
+## Release notes
+
+### Version 0.1.0
+
+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
+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
@@ -0,0 +1,111 @@
+# 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.9.5 REQUIRED)
+find_package(RCMake 0.4.1 REQUIRED)
+find_package(RSys 0.12.1 REQUIRED)
+
+set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${RCMAKE_SOURCE_DIR})
+include(rcmake)
+include(rcmake_runtime)
+
+include_directories(${GMSH_INCLUDE_DIR} ${RSys_INCLUDE_DIR})
+
+################################################################################
+# Configure and define targets
+################################################################################
+set(VERSION_MAJOR 0)
+set(VERSION_MINOR 1)
+set(VERSION_PATCH 0)
+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_LIBRARY} 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)
+
+ 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
@@ -1,10 +0,0 @@
-CC = cc
-PREFIX = /usr/local
-INC = /usr/include
-LIB = /usr/lib
-
-AFLAGS = crs
-WFLAGS = -Wall -Wextra -Wmissing-declarations -Wmissing-prototypes -Wconversion -Wshadow
-CFLAGS = -O2 -std=c89 -pedantic -I$(INC) $(WFLAGS)
-LDFLAGS = -L$(LIB)
-LDLIBS = -lm -lrsys -lgmsh
diff --git a/src/scad.c b/src/scad.c
@@ -13,815 +13,528 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. */
-
#include "scad.h"
-#include <rsys/str.h>
-
-res_T
-scad_init(void)
-{
- res_T res = RES_OK;
- int ierr = 0;
- gmshInitialize(0, NULL, 1, 0, &ierr);
- gmshModelAdd("model", &ierr);
-
- gmshOptionSetNumber("Mesh.StlOneSolidPerSurface", 2, &ierr);
- gmshOptionSetNumber("Mesh.MeshSizeFromPoints", 0, &ierr);
- gmshOptionSetNumber("Mesh.MeshSizeFromCurvature", 1, &ierr);
- gmshOptionSetNumber("Mesh.MinimumElementsPerTwoPi", 36, &ierr);
- gmshOptionSetNumber("Mesh.MeshSizeExtendFromBoundary", 0, &ierr);
-
- if (ierr !=0) goto error;
-
-exit:
- return res;
-error:
- fprintf(stderr,"Can't initialize gmsh !\n");
- goto exit;
-}
+#include "scad_c.h"
+#include "scad_device.h"
+#include "scad_geometry.h"
-res_T
-scad_release(void)
-{
- int ierr = 0;
- gmshFinalize(&ierr);
-
- if (ierr != 0) goto error;
-
-exit:
- return ierr;
-error:
- fprintf(stderr,"Can't release gmsh !\n");
- goto exit;
-}
-
-res_T
-scad_synchronize(void)
-{
- int ierr;
- gmshModelOccSynchronize(&ierr);
- return ierr;
-}
+#include <gmsh/gmshc.h>
-res_T
-scad_run_ui(void)
-{
- int ierr;
- gmshFltkRun(&ierr);
- return ierr;
-}
-
-
-res_T
-scad_concat(scad_geom_T* geom1, const scad_geom_T geom2)
-{
- int res = RES_OK;
- int i;
+#include <rsys/cstr.h>
+#include <rsys/rsys.h>
+#include <rsys/str.h>
+#include <rsys/double3.h>
+#include <rsys/float3.h>
- if (!geom2) {fprintf(stderr,"Can't concat \n"); res = RES_BAD_ARG; goto error;}
+#include <stdio.h>
- for (i=0; i<(int)sa_size(geom2) ; ++i)
- {
- sa_push(*geom1, geom2[i]);
- }
+#define OKP(Expr) if((Expr) < 0) { res=RES_IO_ERR; goto error; }
-exit:
- return res;
-error:
- goto exit;
-}
-
-res_T
-scad_addrectangle
-(const double xyz[3], const double dxdy[2], scad_geom_T* geom)
+static res_T
+write_ascii_stl
+ (const char* filename,
+ const unsigned trg_count,
+ double* const* coord,
+ const size_t* coord_n)
{
res_T res = RES_OK;
- int ierr = 0;
- int tag = 0;
-
- if(!xyz || !dxdy) {
- fprintf(stderr,"Invalid data !\n");
- return RES_BAD_ARG;
- }
+ size_t i;
+ FILE* stl_file = NULL;
+ unsigned cpt;
- tag = gmshModelOccAddRectangle(xyz[0], xyz[1], xyz[2],
- dxdy[0], dxdy[1],
- -1,
- 0,
- &ierr);
+ ASSERT(filename && coord && coord_n);
- if (ierr !=0) goto error;
+ stl_file = fopen(filename, "w");
+ if(!stl_file) {
+ res = RES_IO_ERR;
+ goto error;
+ }
- if (*geom != NULL) *geom = NULL;
- sa_push(*geom,2);
- sa_push(*geom,tag);
+ OKP(fprintf(stl_file, "solid %s\n", filename));
+
+ i = 0;
+ cpt = 0;
+ while(cpt < trg_count) {
+ size_t j;
+ for(j = 0; j < coord_n[i]; j += 9) {
+ int k;
+ double n[3] = { 0,0,0 }, zero[3] = { 0,0,0 };
+ double vtx[3][3];
+ double tmp[3], edge1[3], edge2[3];;
+ d3_set(vtx[0], coord[i]+j+0);
+ d3_set(vtx[1], coord[i]+j+3);
+ d3_set(vtx[2], coord[i]+j+6);
+ d3_sub(edge1, vtx[1], vtx[0]);
+ d3_sub(edge2, vtx[2], vtx[0]);
+ d3_cross(tmp, edge1, edge2);
+ if(d3_eq(tmp, zero)) {
+ log_error(get_device(), "Error: nul triangle in exported geometry.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+ d3_normalize(n, tmp);
+
+ OKP(fprintf(stl_file, " facet normal %g %g %g\n", SPLIT3(n)));
+ OKP(fprintf(stl_file, " outer loop\n"));
+ for(k = 0; k < 3; k++) {
+ OKP(fprintf(stl_file, " vertex %.16g %.16g %.16g\n", SPLIT3(vtx[k])));
+ }
+ OKP(fprintf(stl_file, " endloop\n"));
+ OKP(fprintf(stl_file, " endfacet\n"));
+
+ cpt++;
+ }
+ i++;
+ }
+ OKP(fprintf(stl_file, "endsolid \n"));
exit:
+ if(stl_file) fclose(stl_file);
return res;
error:
- res = ierr;
- fprintf(stderr,"Can't create rectangle !\n");
+ log_error(get_device(), "Error: could not export to STL file '%s': %s\n",
+ filename, res_to_cstr(res));
goto exit;
}
-res_T
-scad_adddisk
-(const double xyz[3], const double rad, scad_geom_T* geom)
+static res_T
+write_binary_stl
+ (const char* filename,
+ const unsigned trg_count,
+ double* const* coord,
+ const size_t* coord_n)
{
res_T res = RES_OK;
- int ierr = 0;
- int tag = 0;
-
- if(!xyz || rad < 0) {
- fprintf(stderr,"Invalid data !\n");
- return RES_BAD_ARG;
- }
-
- tag = gmshModelOccAddDisk(xyz[0], xyz[1], xyz[2], rad, rad, -1, &ierr);
+ size_t i;
+ char header[80] = "Binary STL";
+ FILE* stl_file = NULL;
+ unsigned cpt;
- if (ierr !=0) goto error;
-
- if (*geom != NULL) *geom = NULL;
- sa_push(*geom,2);
- sa_push(*geom,tag);
-
-exit:
- return res;
-error:
- res = ierr;
- fprintf(stderr,"Can't create disk !\n");
- goto exit;
-}
+ ASSERT(filename && coord && coord_n);
-res_T
-scad_addpolygon
-(const double* x, const double* y, const double z, const int n, scad_geom_T* geom)
-{
- res_T res = RES_OK;
- int ierr = 0;
- int i;
- int* points = NULL;
- int* lines = NULL;
- int loop;
- int surf;
-
- if(!x || !y || n<3) {
- fprintf(stderr,"Invalid data !\n");
- return RES_BAD_ARG;
+ stl_file = fopen(filename, "wb");
+ if(!stl_file) {
+ res = RES_IO_ERR;
+ goto error;
}
- for (i=0; i<n; ++i){
- int tag;
- tag = gmshModelOccAddPoint(x[i], y[i], z, -1, -1, &ierr);
- sa_push(points, tag);
- if (ierr !=0) goto error;
+ if(1 != fwrite(header, sizeof(header), 1, stl_file)) {
+ res = RES_IO_ERR;
+ goto error;
}
-
- for (i=0; i<n; ++i){
- int tag;
-
- if (i != (n-1)) {
- tag = gmshModelOccAddLine(points[i],
- points[i+1],
- -1,
- &ierr);
- if (ierr !=0) goto error;
- } else {
- tag = gmshModelOccAddLine(points[i],
- points[0],
- -1,
- &ierr);
- if (ierr !=0) goto error;
- }
- sa_push(lines, tag);
- }
-
- loop = gmshModelOccAddCurveLoop(lines, sa_size(lines), -1, &ierr);
- if (ierr !=0) goto error;
- surf = gmshModelOccAddPlaneSurface(&loop, 1, -1, &ierr);
- if (ierr !=0) goto error;
+ if(1 != fwrite(&trg_count, 4, 1, stl_file)) {
+ res = RES_IO_ERR;
+ goto error;
+ }
- if (*geom != NULL) *geom = NULL;
- sa_push(*geom,2);
- sa_push(*geom,surf);
+ i = 0;
+ cpt = 0;
+ while(cpt < trg_count) {
+ size_t j;
+ for(j = 0; j < coord_n[i]; j += 9) {
+ struct {
+ float n[3];
+ float vrtx[3][3];
+ unsigned short attrib;
+ } trg;
+ float tmp[3], edge1[3], edge2[3];;
+ f3_set_d3(trg.vrtx[0], coord[i]+j+0);
+ f3_set_d3(trg.vrtx[1], coord[i]+j+3);
+ f3_set_d3(trg.vrtx[2], coord[i]+j+6);
+ f3_sub(edge1, trg.vrtx[1], trg.vrtx[0]);
+ f3_sub(edge2, trg.vrtx[2], trg.vrtx[0]);
+ f3_cross(tmp, edge1, edge2);
+ f3_normalize(trg.n, tmp);
+ trg.attrib = 0;
+ if(1 != fwrite(&trg, 50, 1, stl_file)) {
+ res = RES_IO_ERR;
+ goto error;
+ }
+ cpt++;
+ }
+ i++;
+ }
exit:
- if (points) sa_release(points);
- if (lines) sa_release(lines);
+ if(stl_file) fclose(stl_file);
return res;
error:
- res = ierr;
- fprintf(stderr,"Can't create polygon !\n");
+ log_error(get_device(), "Error: could not export to STL file '%s': %s\n",
+ filename, res_to_cstr(res));
goto exit;
}
-res_T
-scad_addbox
-(const double xyz[3], const double dxdydz[3], scad_geom_T* geom)
+static res_T
+write_stl
+ (const char* filename,
+ const unsigned trg_count,
+ double* const* coord,
+ const size_t* coord_n,
+ const int binary)
{
- res_T res = RES_OK;
- int ierr = 0;
- int tag = 0;
-
- if(!xyz || !dxdydz) {
- fprintf(stderr,"Invalid data !\n");
- return RES_BAD_ARG;
- }
-
- tag = gmshModelOccAddBox(xyz[0], xyz[1], xyz[2],
- dxdydz[0], dxdydz[1], dxdydz[2],
- -1,
- &ierr);
-
- if (ierr !=0) goto error;
-
- if (*geom != NULL) *geom = NULL;
- sa_push(*geom,3);
- sa_push(*geom,tag);
-
-exit:
- return res;
-error:
- res = ierr;
- fprintf(stderr,"Can't create box !\n");
- goto exit;
-
+ ASSERT(filename && coord && coord_n);
+ if(binary) return write_binary_stl(filename, trg_count, coord, coord_n);
+ else return write_ascii_stl(filename, trg_count, coord, coord_n);
}
-
+/******************************************************************************
+ * Exported functions
+ *****************************************************************************/
res_T
-scad_addcylinder
-(const double xyz[3], const double axis[3], const double rad, const double angle, scad_geom_T* geom)
+scad_synchronize
+ (void)
{
res_T res = RES_OK;
- int ierr = 0;
- int tag = 0;
+ struct scad_device* dev = get_device();
+ int ierr;
- if(!xyz || !axis) {
- fprintf(stderr,"Invalid data !\n");
- return RES_BAD_ARG;
+ /* Cannot use check_device to avoid an infinite loop! */
+ if(!dev) {
+ /* No logger available for a message */
+ fprintf(stderr,
+ "%s: cannot call API functions if star-cad is not initialized.\n",
+ FUNC_NAME);
+ res = RES_BAD_ARG;
+ goto error;
}
- tag = gmshModelOccAddCylinder(xyz[0],
- xyz[1],
- xyz[2],
- axis[0],
- axis[1],
- axis[2],
- rad,
- -1,
- angle,
- &ierr);
-
- if (ierr !=0) goto error;
-
- if (*geom != NULL) *geom = NULL;
- sa_push(*geom,3);
- sa_push(*geom,tag);
+ gmshModelOccSynchronize(&ierr);
+ get_device()->need_synchro = dev->options.Misc.DebugOpenCascadeSync;
+ ERR(gmsh_err_to_res_T(ierr));
exit:
return res;
error:
- res = ierr;
- fprintf(stderr,"Can't create cylinder !\n");
goto exit;
}
-int
-scad_addsphere
-(const double xyz[3], const double rad, scad_geom_T* geom)
+res_T
+scad_run_ui(void)
{
res_T res = RES_OK;
- int ierr = 0;
- int tag = 0;
+ struct scad_device* dev = get_device();
+ int ierr;
- if(!xyz) {
- fprintf(stderr,"Invalid data !\n");
- return RES_BAD_ARG;
+ /* Cannot use check_device to avoid an infinite loop! */
+ if(!dev) {
+ /* No logger available for a message */
+ fprintf(stderr,
+ "%s: cannot call API functions if star-cad is not initialized.\n",
+ FUNC_NAME);
+ res = RES_BAD_ARG;
+ goto error;
}
- tag = gmshModelOccAddSphere(xyz[0],
- xyz[1],
- xyz[2],
- rad,
- -1,
- -PI/2,
- PI/2,
- 2*PI,
- &ierr);
-
- if (ierr !=0) goto error;
+ if(dev->options.Misc.SynchronizeOnRunUI && get_device()->need_synchro) {
+ ERR(scad_synchronize());
+ }
- if (*geom != NULL) *geom = NULL;
- sa_push(*geom,3);
- sa_push(*geom,tag);
+ gmshFltkRun(&ierr);
+ ERR(gmsh_err_to_res_T(ierr));
exit:
return res;
error:
- res = ierr;
- fprintf(stderr,"Can't create sphere !\n");
goto exit;
}
res_T
-scad_remove(scad_geom_T geom)
+scad_scene_write
+ (const char* name)
{
+ int ierr;
res_T res = RES_OK;
- int ierr = 0;
- gmshModelOccRemove(geom, sa_size(geom), 0, &ierr);
-
- if (ierr !=0) goto error;
-
-exit:
- return res;
-error:
- res = ierr;
- fprintf(stderr,"Can't remove geometry !\n");
- goto exit;
-}
-
-
-res_T
-scad_fuse
-(const scad_geom_T geom1, const scad_geom_T geom2, scad_geom_T* out, const int remove)
-{
- res_T res = RES_OK;
- int ierr, i;
- int* tagout;
- int** map = NULL;
- size_t tagoutn, *mapn, mapnn;
-
- gmshModelOccFuse(geom1, sa_size(geom1),
- geom2, sa_size(geom2),
- &tagout, &tagoutn,
- &map, &mapn, &mapnn,
- -1,
- remove,
- remove,
- &ierr);
-
- if (ierr != 0 ) {fprintf(stderr,"Fuse not possible !\n"); goto error;}
-
- if (*out) sa_release(*out); *out = NULL;
- for (i=0; i<(int)tagoutn; ++i){
- sa_push(*out, tagout[i]);
+ if(!name) {
+ res = RES_BAD_ARG;
+ goto error;
}
-
-exit:
- if (tagout) free(tagout);
- if (mapn) free(mapn);
- if (map) free(map);
- return res;
-error:
- goto exit;
-}
-
-res_T
-scad_cut
-(const scad_geom_T geom1, const scad_geom_T geom2, scad_geom_T* out, const int remove)
-{
- res_T res = RES_OK;
- int ierr, i;
- int* tagout;
- int** map = NULL;
- size_t tagoutn, *mapn, mapnn;
-
- gmshModelOccCut(geom1, sa_size(geom1),
- geom2, sa_size(geom2),
- &tagout, &tagoutn,
- &map, &mapn, &mapnn,
- -1,
- remove,
- remove,
- &ierr);
-
- if (ierr != 0 ) {fprintf(stderr,"Cut not possible !\n"); goto error;}
-
- if (*out) sa_release(*out); *out = NULL;
- for (i=0; i<(int)tagoutn; ++i){
- sa_push(*out, tagout[i]);
- }
+ gmshWrite(name, &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
exit:
- if (tagout) free(tagout);
- if (mapn) free(mapn);
- if (map) free(map);
return res;
error:
goto exit;
}
res_T
-scad_intersect
-(const scad_geom_T geom1, const scad_geom_T geom2, scad_geom_T* out, const int remove)
+scad_stl_export
+ (struct scad_geometry* geometry,
+ const char* prefix,
+ const int reverse,
+ const int binary)
{
+ struct str filename;
+ int* dimTags_to_free = NULL;
+ int* faces_dimTags = NULL;
+ size_t faces_dimTags_n;
+ size_t** nodeTags = NULL;
+ size_t* nodeTags_n = NULL;
+ double** coord = NULL;
+ size_t* coord_n = NULL;
+ double** pCoord = NULL;
+ size_t* pCoord_n = NULL;
+ size_t i, tcount;
+ int dim_ctrl;
+ int ierr = 0;
+ int* data;
+ size_t sz;
+ int str_initialized = 0;
+ struct scad_device* dev = get_device();
+ struct mem_allocator* allocator = NULL;
res_T res = RES_OK;
- int ierr, i;
- int* tagout;
- int** map = NULL;
- size_t tagoutn, *mapn, mapnn;
-
- gmshModelOccIntersect(geom1, sa_size(geom1),
- geom2, sa_size(geom2),
- &tagout, &tagoutn,
- &map, &mapn, &mapnn,
- -1,
- remove,
- remove,
- &ierr);
-
- if (ierr != 0 ) {fprintf(stderr,"Intersection not possible !\n"); goto error;}
-
-
- tagoutn = 0;
- if (tagoutn == 0) { /* try instersect boundary to extract common face */
- int* bound1;
- int* bound2;
- size_t n1, n2;
-
- gmshModelGetBoundary(geom1, sa_size(geom1),
- &bound1, &n1,
- 1,
- 0,
- 0,
- &ierr);
-
- /*if (ierr != 0) { */
- /*if (bound1) free(bound1};*/
- /*goto error:*/
- /*}*/
-
- gmshModelGetBoundary(geom2, sa_size(geom2),
- &bound2, &n2,
- 1,
- 0,
- 0,
- &ierr);
-
- gmshModelOccIntersect(bound1, n1,
- bound2, n2,
- &tagout, &tagoutn,
- &map, &mapn, &mapnn,
- -1,
- NODELETE,
- NODELETE,
- &ierr);
- }
-
- if (*out) sa_release(*out); *out = NULL;
- for (i=0; i<(int)tagoutn; ++i){
- sa_push(*out, tagout[i]);
+ if(!geometry) {
+ res = RES_BAD_ARG;
+ goto error;
}
-exit:
- if (tagout) free(tagout);
- if (mapn) free(mapn);
- if (map) free(map);
- return res;
-error:
- goto exit;
-}
+ ERR(check_device(FUNC_NAME));
+ sz = geometry->gmsh_dimTags_n;
+ data = geometry->gmsh_dimTags;
+ ASSERT(sz > 0 && sz % 2 == 0);
-res_T
-scad_fragment
-(const scad_geom_T geom1, const scad_geom_T geom2, scad_geom_T* out, const int remove)
-{
- res_T res = RES_OK;
- int ierr, i;
- int* tagout;
- int** map = NULL;
- size_t tagoutn, *mapn, mapnn;
-
- gmshModelOccFragment(geom1, sa_size(geom1),
- geom2, sa_size(geom2),
- &tagout, &tagoutn,
- &map, &mapn, &mapnn,
- -1,
- remove,
- remove,
- &ierr);
-
- if (ierr != 0 ) {fprintf(stderr,"Fragment not possible !\n"); goto error;}
-
- if (*out) sa_release(*out); *out = NULL;
- for (i=0; i<(int)tagoutn; ++i){
- sa_push(*out, tagout[i]);
+ allocator = dev->allocator;
+ str_init(allocator, &filename);
+ str_initialized = 1;
+ if(prefix) {
+ ERR(str_set(&filename, prefix));
+ } else {
+ if(str_len(&geometry->name) == 0) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+ ERR(str_copy(&filename, &geometry->name));
+ }
+ ERR(str_append(&filename, ".stl"));
+
+ dim_ctrl = data[0];
+ if(dim_ctrl == 3) { /* geometry is 3D */
+ ERR(scad_synchronize());
+ ERR(gmsh_err_to_res_T(ierr));
+
+ /* TODO: use options to set inward/outward orientation???
+ * Is it even needed???? */
+ for(i = 0; i < sz/2; i++) {
+ ASSERT(data[2*i] == dim_ctrl); /* Only 3D tags */
+ gmshModelMeshSetOutwardOrientation(data[2*i+1], &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+ }
+ if (reverse) gmshModelMeshReverse(data, sz, &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+
+ /* Get tags to be written (boundary of geometry) */
+ gmshModelGetBoundary(data, sz, &faces_dimTags, &faces_dimTags_n, 1, 0, 0, &ierr);
+ dimTags_to_free = faces_dimTags;
+ ERR(gmsh_err_to_res_T(ierr));
+ ASSERT(faces_dimTags_n % 2 == 0);
+ } else { /* Geometry is 2D */
+ /* Tags to be written are the tags of the 2D object itself */
+ faces_dimTags_n = sz;
+ faces_dimTags = data;
+ for(i = 0; i < sz/2; i++) {
+ ASSERT(data[2*i] == dim_ctrl); /* Only 3D tags */
+ }
+ if (reverse) gmshModelMeshReverse(data, sz, &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
}
-exit:
- if (tagout) free(tagout);
- if (mapn) free(mapn);
- if (map) free(map);
- return res;
-error:
- goto exit;
-}
+ /* Allocate room for arrays */
+ nodeTags = MEM_CALLOC(allocator, faces_dimTags_n/2, sizeof(*nodeTags));
+ nodeTags_n = MEM_CALLOC(allocator, faces_dimTags_n/2, sizeof(*nodeTags_n));
+ coord = MEM_CALLOC(allocator, faces_dimTags_n/2, sizeof(*coord));
+ coord_n = MEM_CALLOC(allocator, faces_dimTags_n/2, sizeof(*coord_n));
+ pCoord = MEM_CALLOC(allocator, faces_dimTags_n/2, sizeof(*pCoord));
+ pCoord_n = MEM_CALLOC(allocator, faces_dimTags_n/2, sizeof(*pCoord_n));
+ if(!nodeTags || !nodeTags_n || !coord || !coord_n || !pCoord || !pCoord_n) {
+ res = RES_MEM_ERR;
+ goto error;
+ }
-res_T
-scad_boundary
-(const scad_geom_T geom1, scad_geom_T* out)
-{
- res_T res = RES_OK;
- int ierr,i;
- int* b;
- size_t n;
-
- gmshModelGetBoundary(geom1, sa_size(geom1),
- &b, &n,
- 1,
- 0,
- 0,
- &ierr);
-
- if (ierr != 0 ) {fprintf(stderr,"Get boundary not possible !\n"); goto error;}
-
- if (*out) sa_release(*out); *out = NULL;
- for (i=0; i<(int)n; ++i){
- sa_push(*out, b[i]);
+ tcount = 0;
+ for(i = 0; i < faces_dimTags_n/2; i++) {
+ /* type = 2: 3-node triangles (see src/common/GmshDefines.h) */
+ gmshModelMeshGetNodesByElementType(2, nodeTags+i, nodeTags_n+i,
+ coord+i, coord_n+i, pCoord+i, pCoord_n+i, faces_dimTags[2*i+1], 0, &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+ ASSERT(nodeTags_n[i] % 3 == 0);
+ ASSERT(coord_n[i] == nodeTags_n[i] * 3);
+ tcount += coord_n[i];
}
+ tcount /= 9;
+ ASSERT(tcount <= UINT_MAX);
+
+ ERR(write_stl(str_cget(&filename), (unsigned)tcount, coord, coord_n, binary));
exit:
- if (b) free(b);
+ if(!allocator) {
+ /* Early error, nothing was allocated */
+ return res;
+ }
+ if(str_initialized) str_release(&filename);
+ gmshFree(dimTags_to_free);
+ if(nodeTags) {
+ for(i = 0; i < faces_dimTags_n/2; i++) gmshFree(nodeTags[i]);
+ MEM_RM(allocator, nodeTags);
+ }
+ MEM_RM(allocator, nodeTags_n);
+ if(coord) {
+ for(i = 0; i < faces_dimTags_n/2; i++) gmshFree(coord[i]);
+ MEM_RM(allocator, coord);
+ }
+ MEM_RM(allocator, coord_n);
+ if(pCoord) {
+ for(i = 0; i < faces_dimTags_n/2; i++) gmshFree(pCoord[i]);
+ MEM_RM(allocator, pCoord);
+ }
+ MEM_RM(allocator, pCoord_n);
return res;
error:
+ if(dev) {
+ log_error(dev, "%s: could not export to STL -- %s\n",
+ FUNC_NAME, res_to_cstr(res));
+ }
goto exit;
}
res_T
-scad_translate
-(scad_geom_T geom, const double dxdydz[3])
+scad_stl_export_split
+ (struct scad_geometry* geometry,
+ const char* prefix,
+ const int binary) /* FIXME: unused as we use gmshWrite */
{
+ struct str filename_root;
+ struct str filename;
+ int* tagout = NULL;
+ size_t tagoutn, i;
+ int dimtag[2];
+ int group;
+ int ierr = 0;
+ int* data;
+ size_t sz;
+ int str_initialized = 0;
res_T res = RES_OK;
- int ierr;
- if (!geom || !dxdydz){
- fprintf(stderr,"Invalid data !\n");
- return RES_BAD_ARG;
+ (void)binary;
+
+ if(!geometry) {
+ res = RES_BAD_ARG;
+ goto error;
}
- gmshModelOccTranslate(geom, sa_size(geom),
- dxdydz[0],
- dxdydz[1],
- dxdydz[2],
- &ierr);
+ ERR(check_device(FUNC_NAME));
- if (ierr != 0 ) {fprintf(stderr,"Translation not possible !\n"); goto error;}
+ sz = geometry->gmsh_dimTags_n;
+ data = geometry->gmsh_dimTags;
+ ASSERT(sz % 2 == 0);
-exit:
- return res;
-error:
- res = ierr;
- goto exit;
-}
-
-res_T
-scad_rotate
-(scad_geom_T geom, const double pt[3], const double axis[3], const double angle)
-{
- res_T res = RES_OK;
- int ierr;
-
- if (!geom || !pt || !axis){
- fprintf(stderr,"Invalid data !\n");
- return RES_BAD_ARG;
+ str_init(get_device()->allocator, &filename_root);
+ str_init(get_device()->allocator, &filename);
+ str_initialized = 1;
+ if(prefix) {
+ ERR(str_set(&filename_root, prefix));
+ } else {
+ if(str_len(&geometry->name) == 0) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+ ERR(str_copy(&filename_root, &geometry->name));
}
+ ERR(str_append(&filename_root, "_"));
+
+ if(data[0] == 2) {
+ FOR_EACH(i, 0, sz/2) {
+ ASSERT(data[2*i] == 2);
+ /* When upgrading to gmsh 4.11 the additional arg. name cannot be NULL;
+ * use "" instead */
+ group = gmshModelAddPhysicalGroup(2, &data[2*i+1], 1, -1, &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+ ERR(str_copy(&filename, &filename_root));
+ ERR(str_append_printf(&filename, "%lu.stl", (unsigned long)i));
+ gmshWrite(str_cget(&filename), &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
- gmshModelOccRotate(geom, sa_size(geom),
- pt[0],
- pt[1],
- pt[2],
- axis[0],
- axis[1],
- axis[2],
- angle,
- &ierr);
-
- if (ierr != 0 ) {fprintf(stderr,"Rotation not possible !\n"); goto error;}
+ dimtag[0]=2;
+ dimtag[1]=group;
+ gmshModelRemovePhysicalGroups(dimtag, 2, &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+ }
+ } else {
+ FOR_EACH(i, 0, sz/2) {
+ ASSERT(data[2*i] == 3);
+ gmshModelMeshSetOutwardOrientation(data[2*i+1], &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+ }
-exit:
- return res;
-error:
- res = ierr;
- goto exit;
-}
+ gmshModelGetBoundary(data, sz, &tagout, &tagoutn, 1, 0, 0, &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
-res_T
-scad_extrude(const scad_geom_T geom, const double dxdydz[3], scad_geom_T* out)
-{
- res_T res = RES_OK;
- int ierr, i;
- int* tagout;
- size_t tagoutn;
+ FOR_EACH(i, 0, tagoutn/2){
+ /* When upgrading to gmsh 4.11 the additional arg. name cannot be NULL;
+ * use "" instead */
+ group = gmshModelAddPhysicalGroup(2, tagout + 2*i+1, 1, -1, &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+ ERR(str_copy(&filename, &filename_root));
+ ERR(str_append_printf(&filename, "%lu.stl", (unsigned long)i));
+ gmshWrite(str_cget(&filename), &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
- if (!geom || !dxdydz){
- fprintf(stderr,"Invalid data !\n");
- return RES_BAD_ARG;
- }
-
- gmshModelOccExtrude(geom, sa_size(geom),
- dxdydz[0],
- dxdydz[1],
- dxdydz[2],
- &tagout, &tagoutn,
- NULL, 0,
- NULL, 0,
- 0,
- &ierr);
-
- if (*out) sa_release(*out); *out = NULL;
- for (i=0; i<(int)tagoutn; ++i){
- sa_push(*out, tagout[i]);
+ dimtag[0]=2;
+ dimtag[1]=group;
+ gmshModelRemovePhysicalGroups(dimtag, 2, &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+ }
}
- if (ierr != 0 ) {fprintf(stderr,"Extrusion not possible !\n"); goto error;}
-
exit:
+ if(str_initialized) {
+ str_release(&filename_root);
+ str_release(&filename);
+ }
+ gmshFree(tagout);
return res;
error:
- res = ierr;
goto exit;
}
-res_T
-scad_conformal_mesh(void)
-{
- int ierr = 0;
- int* dimTags;
- size_t dimTags_n;
-
- gmshModelOccSynchronize(&ierr);
- gmshModelOccGetEntities(&dimTags, &dimTags_n, 3, &ierr);
- if ( ierr == 0 && dimTags_n > 2) gmshModelOccRemoveAllDuplicates(&ierr);
- gmshModelOccSynchronize(&ierr);
- gmshModelMeshGenerate(2, &ierr);
-
- return ierr;
-}
-
-
res_T
-scad_stl_export(const scad_geom_T geom, char *prefix)
+scad_scene_mesh
+ (void)
{
+ int ierr = 0;
res_T res = RES_OK;
- int ierr;
- int i;
- int* tagout = NULL;
- int* tags = NULL;
- size_t tagoutn;
- int group, dimtag[2];
- struct str filename;
- str_init(NULL, &filename);
- str_set(&filename, prefix);
- str_append(&filename, ".stl");
+ ERR(check_device(FUNC_NAME));
- if (geom[0] == 2) {
- tagoutn = sa_size(geom);
- for(i=0; i<(int)tagoutn/2; ++i){
- sa_push(tags, geom[2*i + 1]);
- }
-
- group = gmshModelAddPhysicalGroup(2, tags, tagoutn/2,
- -1,
- &ierr);
- if (ierr !=0) goto error;
-
- } else {
-
- for (i=0; i<(int)sa_size(geom)/2; ++i)
- {
- gmshModelMeshSetOutwardOrientation(geom[2*i+1], &ierr);
- }
-
- gmshModelGetBoundary(geom, sa_size(geom),
- &tagout, &tagoutn,
- 1,
- 0,
- 0,
- &ierr);
-
- if (ierr !=0) goto error;
-
- for(i=0; i<(int)tagoutn/2; ++i){
- sa_push(tags, tagout[2*i + 1]);
- }
-
- group = gmshModelAddPhysicalGroup(2, tags, tagoutn/2,
- -1,
- &ierr);
- if (ierr !=0) goto error;
- }
-
- gmshWrite(str_get(&filename), &ierr);
- if (ierr !=0) goto error;
-
- dimtag[0]=2;
- dimtag[1]=group;
- gmshModelRemovePhysicalGroups(dimtag, 2, &ierr);
- if (ierr !=0) goto error;
+ gmshModelMeshGenerate(2, &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
exit:
- str_release(&filename);
- if (tagout) free(tagout);
- if (tags) sa_release(tags);
return res;
error:
goto exit;
}
res_T
-scad_stl_export_split(const scad_geom_T geom, char *prefix)
+scad_scene_partition
+ (void)
{
+ int ierr = 0;
res_T res = RES_OK;
- int ierr;
- int i;
- int* tagout = NULL;
- int* tags = NULL;
- size_t tagoutn;
- int group, dimtag[2];
-
- if (geom[0] == 2) {
- tagoutn = sa_size(geom);
- for(i=0; i<(int)tagoutn/2; ++i){
- struct str filename;
- char num[32];
-
- group = gmshModelAddPhysicalGroup(2, &geom[2*i+1], 1,
- -1,
- &ierr);
- if (ierr !=0) goto error;
-
- str_init(NULL, &filename);
- str_set(&filename, prefix);
- str_append_char(&filename, '_');
- sprintf(num, "%d", i);
- str_append(&filename, num);
- str_append(&filename, ".stl");
- gmshWrite(str_get(&filename), &ierr);
- if (ierr !=0) {str_release(&filename); goto error;}
- dimtag[0]=2;
- dimtag[1]=group;
- gmshModelRemovePhysicalGroups(dimtag, 2, &ierr);
- if (ierr !=0) {str_release(&filename); goto error;}
-
- str_release(&filename);
- }
-
- } else {
-
- for (i=0; i<(int)sa_size(geom)/2; ++i)
- {
- gmshModelMeshSetOutwardOrientation(geom[2*i+1], &ierr);
- }
-
- gmshModelGetBoundary(geom, sa_size(geom),
- &tagout, &tagoutn,
- 1,
- 0,
- 0,
- &ierr);
-
- if (ierr !=0) goto error;
-
- for(i=0; i<(int)tagoutn/2; ++i){
- struct str filename;
- char num[32];
-
-
- group = gmshModelAddPhysicalGroup(2, &tagout[2*i+1], 1,
- -1,
- &ierr);
- if (ierr !=0) goto error;
-
- str_init(NULL, &filename);
- str_set(&filename, prefix);
- str_append_char(&filename, '_');
- sprintf(num, "%d", i);
- str_append(&filename, num);
- str_append(&filename, ".stl");
- gmshWrite(str_get(&filename), &ierr);
- if (ierr !=0) {str_release(&filename); goto error;}
+ ERR(check_device(FUNC_NAME));
- dimtag[0]=2;
- dimtag[1]=group;
- gmshModelRemovePhysicalGroups(dimtag, 2, &ierr);
- if (ierr !=0) {str_release(&filename); goto error;}
-
- str_release(&filename);
- }
- }
+ gmshModelOccRemoveAllDuplicates(&ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+ gmshModelOccSynchronize(&ierr);
+ ERR(gmsh_err_to_res_T(ierr));
exit:
- if (tagout) free(tagout);
- if (tags) sa_release(tags);
return res;
error:
goto exit;
diff --git a/src/scad.h b/src/scad.h
@@ -12,121 +12,437 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. */
-
#ifndef SCAD_H
#define SCAD_H
-#include <stdio.h>
-#include <stdlib.h>
-#include <gmshc.h>
#include <rsys/rsys.h>
-#include <rsys/stretchy_array.h>
-#include <rsys/math.h>
-#include <string.h>
-
-/* wrapping of dimTags gmsh description */
-typedef int* scad_geom_T;
-#define SCAD_GEOM_NULL_ NULL
-static const scad_geom_T SCAD_GEOM_NULL = SCAD_GEOM_NULL_;
-
-/* remove the handler but not the geomtry */
-/* to remove geometry, use scad_geom_remove */
-static FINLINE
-res_T scad_geom_release(scad_geom_T geom)
-{
- sa_release(geom);
- return RES_OK;
-}
-
-/*****************************************************************************/
-/*****************************************************************************/
-/*****************************************************************************/
-res_T
-scad_init(void);
-
-res_T
-scad_release(void);
-
-res_T
-scad_synchronize(void);
-
-res_T
-scad_run_ui(void);
-
-res_T
-scad_addrectangle
-(const double xyz[3], const double dxdy[2], scad_geom_T* geom);
-
-res_T
-scad_adddisk
-(const double xyz[3], const double rad, scad_geom_T* geom);
-
-res_T
-scad_addpolygon
-(const double* x, const double* y, const double z, const int n, scad_geom_T* geom);
-
-
-res_T
-scad_addbox
-(const double xyz[3], const double dxdydz[3], scad_geom_T* geom);
-
-res_T
-scad_addcylinder
-(const double xyz[3], const double axis[3], const double rad, const double angle, scad_geom_T* geom);
-
-res_T
-scad_addsphere
-(const double xyz[3], const double rad, scad_geom_T* geom);
-res_T
-scad_remove(scad_geom_T geom);
-
-res_T
-scad_concat(scad_geom_T* geom1, const scad_geom_T geom2);
-
-#define DELETE 1
-#define NODELETE 0
-res_T
-scad_fuse
-(const scad_geom_T geom1, const scad_geom_T geom2, scad_geom_T* out, const int remove);
-
-res_T
-scad_cut
-(const scad_geom_T geom1, const scad_geom_T geom2, scad_geom_T* out, const int remove);
-
-res_T
-scad_intersect
-(const scad_geom_T geom1, const scad_geom_T geom2, scad_geom_T* out, const int remove);
-
-res_T
-scad_fragment
-(const scad_geom_T geom1, const scad_geom_T geom2, scad_geom_T* out, const int remove);
-
-res_T
-scad_boundary
-(const scad_geom_T geom1, scad_geom_T* out);
-
-res_T
-scad_translate
-(scad_geom_T geom, const double dxdydz[3]);
-
-res_T
-scad_rotate
-(scad_geom_T geom, const double pt[3], const double axis[3], const double angle);
-
-res_T
-scad_extrude(const scad_geom_T geom, const double dxdydz[3], scad_geom_T* out);
-
-res_T
-scad_conformal_mesh(void);
-
-res_T
-scad_stl_export(const scad_geom_T geom, char *prefix);
-
-res_T
-scad_stl_export_split(const scad_geom_T geom, char *prefix);
-/*****************************************************************************/
-/*****************************************************************************/
-/*****************************************************************************/
-#endif /* SCAD_H */
+/* Library symbol management */
+#if defined(SCAD_SHARED_BUILD) /* Build shared library */
+ #define SCAD_API extern EXPORT_SYM
+#elif defined(SCAD_STATIC) /* Use/build static library */
+ #define SCAD_API extern LOCAL_SYM
+#else /* Use shared library */
+ #define SCAD_API extern IMPORT_SYM
+#endif
+
+/* Helper macro that asserts if the invocation of the scad function `Func'
+ * returns an error. One should use this macro on scad function calls for which
+ * no explicit error checking is performed */
+#ifndef NDEBUG
+ #define SCAD(Func) ASSERT(scad_ ## Func == RES_OK)
+#else
+ #define SCAD(Func) scad_ ## Func
+#endif
+
+/* Forward declarations */
+struct mem_allocator;
+struct logger;
+struct str;
+
+/* Forward declaration of scad opaque data types */
+struct scad_geometry; /* Wrapping of dimTags gmsh description */
+
+/* Verbosity levels */
+enum scad_verbosity_levels {
+ scad_verbosSCAD_ity_fatal_errors = 0,
+ Scad_verbosity_errors = 1,
+ Scad_verbosity_warnings = 2,
+ Scad_verbosity_direct = 3,
+ Scad_verbosity_information = 4,
+ Scad_verbosity_status = 5,
+ Scad_verbosity_debug = 99
+};
+
+/* Mesh algorithms */
+enum scad_mesh_algorithm {
+ Scad_meshAdapt = 1,
+ Scad_Delaunay = 5,
+ Scad_frontal_Delaunay = 6
+};
+
+enum scad_sizes_extend_from_boundary {
+ Scad_never = 0,
+ Scad_surfaces_and_volumes = 1,
+ Scad_surfaces_and_volumes_smallest = 2,
+ Scad_surfaces_only = -2,
+ Scad_volumes_only = -3
+};
+
+enum scad_stl_solids {
+ Scad_single_solid = 0,
+ Scad_one_solid_per_surface = 1,
+ Scad_one_solid_per_physical_surface = 2
+};
+
+enum scad_log_refcounting {
+ Scad_log_none,
+ Scad_log_only_undeleted,
+ Scad_log_all
+};
+
+/* A type to specify options for the gmsh library */
+struct scad_options {
+ struct {
+ enum scad_mesh_algorithm Algorithm;
+ enum scad_sizes_extend_from_boundary MeshSizeExtendFromBoundary;
+ double MeshSizeFactor;
+ double MeshSizeFromCurvature;
+ int MeshSizeFromPoints;
+ double MeshSizeMax;
+ double MeshSizeMin;
+ double Smoothing;
+ enum scad_stl_solids StlOneSolidPerSurface;
+ } Mesh;
+ struct {
+ enum scad_verbosity_levels Verbosity;
+ int ExpertMode; /* Avoid user interaction on some option combinations */
+ } General;
+ struct {
+ int OCCParallel;
+ } Geometry;
+ struct {
+ int Step; /* Run UI when entering any scad API function; requires a FLTK-enabled gmsh build */
+ int SynchronizeOnRunUI;
+ enum scad_log_refcounting LogOpenCascadeTagsRefCounting;
+ int DebugOpenCascadeSync; /* Systematic call to synchronize; if results change there is a sync bug in star-cad! */
+ } Misc;
+};
+
+#define SCAD_DEFAULT_OPTIONS__ \
+ { { Scad_frontal_Delaunay, Scad_surfaces_and_volumes, 1, 36, 1, 1e+22, 0, \
+ 1, Scad_one_solid_per_physical_surface }, \
+ { Scad_verbosity_errors, 1 }, \
+ { 1 }, \
+ { 0, Scad_log_none, 0, 0 } \
+ }
+
+static const struct scad_options SCAD_DEFAULT_OPTIONS = SCAD_DEFAULT_OPTIONS__;
+
+BEGIN_DECLS
+
+/*******************************************************************************
+ * Init and Finalize calls.
+ * All other API calls must be enclosed between Init and Finalize.
+ ******************************************************************************/
+SCAD_API res_T
+scad_initialize
+ (struct logger* logger, /* May be NULL <=> use default logger */
+ struct mem_allocator* allocator, /* May be NULL <=> use default allocator */
+ const int verbose); /* Define the level of verbosity:
+ 0 = no logs,
+ 1 = errors only,
+ 2 = errors and warnings,
+ 3 = errors, warnings, and informative messages */
+
+SCAD_API res_T
+scad_finalize
+ (void);
+
+SCAD_API res_T
+scad_set_options
+ (const struct scad_options* options); /* May be NULL: set default */
+
+/* Explicitly synchronize the current state with regard to recent geometry changes.
+ * Synchronize calls should be automatically triggered when needed.
+ * Only provided as a way to check for auto-synchronize bugs! */
+SCAD_API res_T
+scad_synchronize
+ (void);
+
+/*******************************************************************************
+ * Geometry API - A geometry is a primitive, a group of primitives, or the
+ * result of an operation on geometries.
+ * If provided, names must be unique.
+ ******************************************************************************/
+
+SCAD_API res_T
+scad_geometry_delete
+ (struct scad_geometry* geom);
+
+SCAD_API res_T
+scad_scene_clear
+ (void);
+
+/* Get the number of components of the geometry `geom' */
+SCAD_API res_T
+scad_geometry_get_count
+ (const struct scad_geometry* geom,
+ size_t* count);
+
+/* Get a pointer to `geom's name.
+ * Note that this reference is only valid during the lifetime of `geom' (don't
+ * use name after deleting `geom') */
+SCAD_API res_T
+scad_geometry_get_name
+ (const struct scad_geometry* geom,
+ const char** name);
+
+/* Swap names of `geom1' and `geom2' */
+SCAD_API res_T
+scad_geometry_swap_names
+ (struct scad_geometry* geom1,
+ struct scad_geometry* geom2);
+
+/* Get the `mass' of the geometry `geom'. It means area for a 2D geometry and
+ * volume for a 3D geometry. */
+SCAD_API res_T
+scad_geometry_get_mass
+ (struct scad_geometry* geom,
+ double* mass);
+
+/* Get the center of mass of the various components of the geometry.
+ * Note that `center' must be allocated be the caller with enough room for (at
+ * least) 3 times the count of geom (scad_geometry_ge_count) doubles */
+SCAD_API res_T
+scad_geometry_get_centerofmass
+ (struct scad_geometry* geom,
+ double* center);
+
+/* Add a rectangle to the scene, defined by a point `xyz' and
+ * `dxdy' the extents along the x-, y-axes. */
+SCAD_API res_T
+scad_add_rectangle
+ (const char* name, /* Can be NULL */
+ const double xyz[3],
+ const double dxdy[2],
+ struct scad_geometry** rectangle);
+
+/* Add a disk in (xy) plane to the scene, defined by a the center `xyz' and
+ * `radius'. */
+SCAD_API res_T
+scad_add_disk
+ (const char* name, /* Can be NULL */
+ const double xyz[3],
+ const double radius,
+ struct scad_geometry** disk);
+
+/* Add a polygonal surface in (xy) plane to the scene at elevation z */
+SCAD_API res_T
+scad_add_polygon
+ (const char* name, /* Can be NULL */
+ void (*get_position)(const size_t ivert, double pos[2], void* data),
+ void* data, /* Custom data; can be NULL if get_position don't use it */
+ const double z,
+ const size_t count, /* size of x and y arrays */
+ struct scad_geometry** polygon);
+
+/* Add a parallelepipedic box to the scene, defined by a point `xyz' and
+ * `dxdydz' the extents along the x-, y- and z-axes. */
+SCAD_API res_T
+scad_add_box
+ (const char* name, /* Can be NULL */
+ const double xyz[3],
+ const double dxdydz[3],
+ struct scad_geometry** box);
+
+/* Add a cylinder to the scene, defined by the center `xyz' of its first
+ * circular face, the vector `axis' defining its axis and its radius `rad'. The
+ * `angle' argument defines the angular opening (from 0 to 2*Pi). */
+SCAD_API res_T
+scad_add_cylinder
+ (const char* name, /* Can be NULL */
+ const double xyz[3],
+ const double axis[3],
+ const double radius,
+ const double angle,
+ struct scad_geometry** cylinder);
+
+/* Add a sphere of center `xyz' and radius `rad' to the scene. */
+SCAD_API res_T
+scad_add_sphere
+ (const char* name, /* Can be NULL */
+ const double xyz[3],
+ const double radius,
+ struct scad_geometry** sphere);
+
+/* Compute the boolean union (the fusion) of the geometries in `geometries' and
+ * `tools'. */
+SCAD_API res_T
+scad_fuse_geometries
+ (const char* name, /* Can be NULL */
+ struct scad_geometry** geometries,
+ const size_t geometries_count,
+ struct scad_geometry** tools,
+ const size_t tools_count,
+ struct scad_geometry** out_geometry);
+
+/* Compute the boolean difference between the geometries in `geometries' and
+ * `tools'. */
+SCAD_API res_T
+scad_cut_geometries
+ (const char* name, /* Can be NULL */
+ struct scad_geometry** geometries,
+ const size_t geometries_count,
+ struct scad_geometry** tools,
+ const size_t tools_count,
+ struct scad_geometry** out_geometry);
+
+/* Compute the boolean intersection (the common parts) of the geometries
+ * in `geometries' and `tools'. */
+SCAD_API res_T
+scad_intersect_geometries
+ (const char* name, /* Can be NULL */
+ struct scad_geometry** geometries,
+ const size_t geometries_count,
+ struct scad_geometry** tools,
+ const size_t tools_count,
+ struct scad_geometry** out_geometry);
+
+/* compute boundary intersection (the common part) of the geometries in
+ * `geometries' and `tools'. */
+SCAD_API res_T
+scad_geometries_common_boundaries
+ (const char* name, /* Can be NULL */
+ struct scad_geometry** geometries,
+ const size_t geometries_count,
+ struct scad_geometry** tools,
+ const size_t tools_count,
+ struct scad_geometry** out_geometry);
+
+/* Compute the boolean fragments (general fuse) resulting from the
+ * intersection of the geometries in `geometries', making all interfaces
+ * conformal.
+ * If overlapping is allowed `out_geometries' is constructed and the new
+ * geometries are created unnamed; if overlapping is not allowed
+ * `out_geometries' remains unchanged and can be NULL.
+ * When applied to geometries of different dimensions, the lower dimensional
+ * geometries will be automatically embedded in the higher dimensional
+ * geometries if they are not on their boundary. */
+SCAD_API res_T
+scad_geometries_partition
+ (struct scad_geometry** geometries,
+ const size_t geometries_count,
+ const int allow_overlapping,
+ struct scad_geometry** out_geometries); /* Can be NULL if overlapping disallowed */
+
+/* Partition the whole scene.
+ * Overlapping is not detected and newly created objects are not returned. */
+SCAD_API res_T
+scad_scene_partition
+ (void);
+
+SCAD_API res_T
+scad_fragment_geometries
+ (const char* name, /* Can be NULL */
+ struct scad_geometry** geometries,
+ const size_t geometries_count,
+ struct scad_geometry** tools,
+ const size_t tools_count,
+ struct scad_geometry** out_geometry);
+
+/* Get the boundary of the geometry `geom'. */
+SCAD_API res_T
+scad_geometry_boundary
+ (const char* name, /* Can be NULL */
+ struct scad_geometry** geometries,
+ const size_t geometries_count,
+ struct scad_geometry** out_boundary);
+
+/* copy the geometry `geom'. */
+SCAD_API res_T
+scad_geometry_copy
+ (const struct scad_geometry* geom,
+ const char* name, /* Can be NULL */
+ struct scad_geometry** out_copy);
+
+/* Change the name of geometry `geom'. */
+SCAD_API res_T
+scad_geometry_rename
+ (struct scad_geometry* geom,
+ const char* name); /* Can be NULL */
+
+/* Translate the geometry `geom' along (`dx', `dy', `dz'). */
+SCAD_API res_T
+scad_geometry_translate
+ (struct scad_geometry* geom,
+ const double dxdydz[3]);
+
+/* Rotate the geometry `geom' by `angle' radians around the axis of revolution
+ * defined by the point `pt' and the direction `dir'. */
+SCAD_API res_T
+scad_geometry_rotate
+ (struct scad_geometry* geom,
+ const double pt[3],
+ const double dir[3],
+ const double angle);
+
+/* Extrude the geometry `geom' using a translation along (`dx', `dy', `dz').*/
+SCAD_API res_T
+scad_geometry_extrude
+ (const struct scad_geometry* geom,
+ const char* name, /* Can be NULL */
+ const double dxdydz[3],
+ struct scad_geometry** out_geometry);
+
+/* Return a list of geometries which form the geometry geom */
+SCAD_API res_T
+scad_geometry_explode
+ (const struct scad_geometry* geom,
+ const char* prefix_name, /* Can be NULL */
+ struct scad_geometry*** out_geometry,
+ size_t* out_geometry_n);
+
+/* Import a step model (`filename'). The imported geometries are recorded in
+ * `out_geometry'.
+ * Note that `out_geometry' is allocated using the allocator provided when
+ * initializing star-cad and should be freed accordingly. */
+SCAD_API res_T
+scad_step_import
+ (const char* filename, /* name of step file */
+ const char* name, /* Can be NULL */
+ struct scad_geometry*** out_geometry,
+ size_t* out_geometry_n);
+
+/* Export the geometry `geom' to an STL file.
+ * If `prefix' is provided it is used to name the file (just adding .stl),
+ * otherwise the geometry name is used instead (and it is an error if neither
+ * prefix nor the geometry name are defined). The file format is either binary
+ * or ascii, depending on the value of the `binary' argument. */
+SCAD_API res_T
+scad_stl_export
+ (struct scad_geometry* geom,
+ const char* prefix, /* Can be NULL if geometry was named: use geometry name */
+ const int reverse, /* set `1' to reverse mesh */
+ const int binary); /* File format */
+
+SCAD_API res_T
+scad_stl_export_split
+ (struct scad_geometry* geom,
+ const char* prefix, /* Can be NULL if geometry was named: use geometry name */
+ const int binary); /* File format */
+
+
+SCAD_API res_T
+scad_scene_write
+ (const char* name);
+
+SCAD_API res_T /* FIXME remove this */
+scad_run_ui
+ (void);
+
+SCAD_API res_T
+scad_scene_mesh
+ (void);
+
+SCAD_API res_T
+scad_geometry_normal
+ (struct scad_geometry* geom,
+ double p[3],
+ double N[3],
+ const char* name, /* Can be NULL */
+ struct scad_geometry** out_geometry);
+
+/* Scale the geometry by * factors `scale' along the three coordinate axes;
+ * use `center', as the center of the homothetic transformation. */
+SCAD_API res_T
+scad_geometry_dilate
+ (struct scad_geometry* geom,
+ double center[3],
+ double scale[3]);
+
+
+END_DECLS
+
+#endif /* SCAD_H */
diff --git a/src/scad_c.h b/src/scad_c.h
@@ -0,0 +1,36 @@
+/* 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/>. */
+
+#ifndef SCAD_C_H
+#define SCAD_C_H
+
+#include <rsys/rsys.h>
+
+#define ERR(Expr) if((res = (Expr)) != RES_OK) goto error; else (void)0
+
+static INLINE res_T
+gmsh_err_to_res_T(const int ierr)
+{
+ res_T res = RES_OK;
+
+ switch(ierr) {
+ /* TODO identify more precisely the gmsh errors */
+ case 0: res = RES_OK; break;
+ default: res = RES_UNKNOWN_ERR; break;
+ }
+ return res;
+}
+
+#endif
diff --git a/src/scad_device.c b/src/scad_device.c
@@ -0,0 +1,434 @@
+/* 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/>. */
+
+#include "rsys/str.h"
+#include "scad.h"
+#include "scad_c.h"
+#include "scad_device.h"
+#include "scad_geometry.h"
+
+#include <rsys/logger.h>
+#include <rsys/mem_allocator.h>
+#include <rsys/ref_count.h>
+#include <rsys/cstr.h>
+
+#include <gmsh/gmshc.h>
+#include <rsys/rsys.h>
+
+/*******************************************************************************
+ * Local functions
+ ******************************************************************************/
+static void
+device_release(struct scad_device* dev)
+{
+ struct htable_geometries tmp;
+ struct htable_geometries_iterator it, end;
+ int log, empty;
+ enum scad_log_refcounting option;
+ enum log_type log_type;
+
+ ASSERT(dev);
+
+ option = dev->options.Misc.LogOpenCascadeTagsRefCounting;
+ empty = htable_geometries_is_empty(&dev->allgeom);
+ log_type = empty ? LOG_OUTPUT : LOG_WARNING;
+ log = (option == Scad_log_all) || (!empty && option == Scad_log_only_undeleted);
+
+ /* Duplicate the htable we iterate on as dev->allgeom will be altered during
+ * the process (through calls to geometry_release) */
+ htable_geometries_init(dev->allocator, &tmp);
+ CHK(RES_OK == htable_geometries_copy(&tmp, &dev->allgeom));
+ htable_geometries_begin(&tmp, &it);
+ htable_geometries_end(&tmp, &end);
+ if(log && empty) {
+ logger_print(dev->logger, log_type, "No scad geometry.\n");
+ }
+ while(!htable_geometries_iterator_eq(&it, &end)) {
+ struct scad_geometry* geom = *htable_geometries_iterator_key_get(&it);
+ CHK(RES_OK == geometry_release(log, log_type, geom));
+ htable_geometries_iterator_next(&it);
+ }
+ htable_names_release(&dev->geometry_names);
+ htable_tags2geom_release(&dev->tags2geom[0]);
+ htable_tags2geom_release(&dev->tags2geom[1]);
+ htable_geometries_release(&dev->allgeom);
+ if(log) {
+ logger_print(dev->logger, log_type, "End finalizing scad.\n");
+ }
+ MEM_RM(dev->allocator, dev);
+ htable_geometries_release(&tmp);
+}
+
+void
+log_error(struct scad_device* dev, const char* msg, ...)
+{
+ va_list vargs_list;
+ ASSERT(dev && msg);
+ if(dev->verbose < 1) return;
+ va_start(vargs_list, msg);
+ log_msg(dev, LOG_ERROR, msg, vargs_list);
+ va_end(vargs_list);
+}
+
+void
+log_warning(struct scad_device* dev, const char* msg, ...)
+{
+ va_list vargs_list;
+ ASSERT(dev && msg);
+ if(dev->verbose < 2) return;
+ va_start(vargs_list, msg);
+ log_msg(dev, LOG_WARNING, msg, vargs_list);
+ va_end(vargs_list);
+}
+
+
+void
+log_message(struct scad_device* dev, const char* msg, ...)
+{
+ va_list vargs_list;
+ ASSERT(dev && msg);
+ if(dev->verbose < 3) return;
+ va_start(vargs_list, msg);
+ log_msg(dev, LOG_OUTPUT, msg, vargs_list);
+ va_end(vargs_list);
+}
+
+/*******************************************************************************
+ * The unique device in scad-cad
+ ******************************************************************************/
+static struct scad_device* g_device = NULL;
+
+/*******************************************************************************
+ * Exported scad_device functions
+ ******************************************************************************/
+res_T
+check_device
+ (const char* function_name)
+{
+ res_T res = RES_OK;
+ ASSERT(function_name);
+
+ if(!g_device) {
+ /* No logger available for a message */
+ fprintf(stderr,
+ "%s: cannot call API functions if star-cad is not initialized.\n",
+ function_name);
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ if(get_device()->need_synchro) {
+ ERR(scad_synchronize());
+ }
+
+ if(g_device->options.Misc.Step) {
+ ERR(scad_run_ui());
+ }
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+struct scad_device*
+get_device
+ (void)
+{
+ return g_device;
+}
+
+res_T
+device_register_tags
+ (struct scad_geometry* geom)
+{
+ res_T res = RES_OK;
+ int* dimTags;
+ size_t count, i;
+ struct scad_device* dev = get_device();
+ int log = (dev->options.Misc.LogOpenCascadeTagsRefCounting == Scad_log_all);
+
+ ASSERT(geom);
+
+ dimTags = geom->gmsh_dimTags;
+ count = geom->gmsh_dimTags_n;
+ if(log) {
+ if(str_is_empty(&geom->name)) {
+ log_message(dev, "Registering tags for unnamed geometry %p.\n",
+ (void*)geom);
+ } else {
+ log_message(dev, "Registering tags for geometry '%s'.\n",
+ str_cget(&geom->name));
+ }
+ }
+
+ for(i = 0; i < count; i += 2) {
+ int dim = dimTags[i];
+ int tag = dimTags[i+1];
+ struct htable_tags2geom* t2g;
+ struct htable_geometries* geoms;
+ char one = 1;
+ CHK(dim == 2 || dim == 3); /* other dims not managed yet */
+ /* Add geom to the geometries that use tag@dim */
+ t2g = g_device->tags2geom + (dim-2);
+ geoms = htable_tags2geom_find(t2g, &tag);
+ if(!geoms) {
+ /* First geom using this tag: create the table */
+ struct htable_geometries g;
+ htable_geometries_init(g_device->allocator, &g);
+ ERR(htable_tags2geom_set(t2g, &tag, &g));
+ geoms = htable_tags2geom_find(t2g, &tag);
+ ASSERT(geoms);
+ if(log) {
+ log_message(dev, "New dim %d tag %d (count set to 1).\n", dim, tag);
+ }
+ } else {
+ if(log) {
+ size_t n = htable_geometries_size_get(geoms);
+ if(n > 0) {
+ log_message(dev, "Dim %d tag %d (count increased to %lu).\n",
+ dim, tag, n+1);
+ } else {
+ log_message(dev, "Reuse dim %d tag %d (count set to 1).\n", dim, tag);
+ }
+ }
+ }
+ ASSERT(!htable_geometries_find(geoms, &geom));
+ ERR(htable_geometries_set(geoms, &geom, &one));
+ ASSERT(htable_geometries_size_get(geoms) >= 1);
+ }
+
+end:
+ return res;
+error:
+ goto end;
+}
+
+res_T
+device_unregister_tags
+ (const int log,
+ const enum log_type log_type,
+ struct scad_geometry* geom)
+{
+ res_T res = RES_OK;
+ int* dimTags;
+ size_t count, i;
+ struct scad_device* dev = get_device();
+
+ ASSERT(geom);
+
+ dimTags = geom->gmsh_dimTags;
+ count = geom->gmsh_dimTags_n;
+
+ if(log) {
+ if(str_is_empty(&geom->name)) {
+ logger_print(dev->logger, log_type,
+ "Unregistering tags for unnamed geometry %p.\n", (void*)geom);
+ } else {
+ logger_print(dev->logger, log_type,
+ "Unregistering tags for geometry '%s'.\n", str_cget(&geom->name));
+ }
+ }
+
+ for(i = 0; i < count; i += 2) {
+ int dim = dimTags[i];
+ int tag = dimTags[i+1];
+ int ierr;
+ struct htable_tags2geom* t2g;
+ struct htable_geometries* geoms;
+ size_t n;
+ CHK(dim == 2 || dim == 3); /* other dims not managed yet */
+ t2g = g_device->tags2geom + (dim-2);
+ geoms = htable_tags2geom_find(t2g, &tag);
+ n = htable_geometries_erase(geoms, &geom);
+ ASSERT(geoms && n == 1); (void)n;
+ n = htable_geometries_size_get(geoms);
+ if(n > 0) {
+ if(log) {
+ logger_print(dev->logger, log_type,
+ "Dim %d tag %d (count decreased to %lu).\n", dim, tag, (unsigned long)n);
+ }
+ continue;
+ }
+ /* The gmsh geometry with tag 'tag' is not in use anymore: release it */
+ if(log) {
+ logger_print(dev->logger, log_type, "Dim %d tag %d removed.\n", dim, tag);
+ }
+
+ gmshModelOccRemove(dimTags+i, 2, 1, &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+ }
+
+end:
+ return res;
+error:
+ goto end;
+}
+
+/*******************************************************************************
+ * API scad_device functions
+ ******************************************************************************/
+res_T
+scad_initialize
+ (struct logger* logger,
+ struct mem_allocator* mem_allocator,
+ const int verbose)
+{
+ struct mem_allocator* allocator;
+ res_T res = RES_OK;
+ int ierr;
+
+ if(g_device != NULL) {
+ log_error(g_device, "scad-star is already initialized.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ if(0 > verbose || verbose > 3) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ gmshInitialize(0, NULL, 1, 0, &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+
+ allocator = mem_allocator ? mem_allocator : &mem_default_allocator;
+ g_device
+ = (struct scad_device*)MEM_CALLOC(allocator, 1, sizeof(struct scad_device));
+ if(!g_device) {
+ res = RES_MEM_ERR;
+ goto error;
+ }
+ g_device->logger = logger ? logger : LOGGER_DEFAULT;
+ g_device->allocator = allocator;
+ g_device->need_synchro = g_device->options.Misc.DebugOpenCascadeSync;
+ g_device->verbose = verbose;
+ htable_names_init(allocator, &g_device->geometry_names);
+ htable_geometries_init(allocator, &g_device->allgeom);
+ htable_tags2geom_init(allocator, &g_device->tags2geom[0]);
+ htable_tags2geom_init(allocator, &g_device->tags2geom[1]);
+ /* Init to default */
+ scad_set_options(NULL);
+
+exit:
+ return res;
+error:
+ if(g_device) {
+ device_release(g_device);
+ g_device = NULL;
+ }
+ goto exit;
+}
+
+res_T
+scad_finalize
+ (void)
+{
+ res_T res = RES_OK;
+ int ierr;
+ struct scad_device* dev = get_device();
+ int log, empty;
+ enum scad_log_refcounting option;
+ enum log_type log_type;
+
+ ERR(check_device(FUNC_NAME));
+ option = dev->options.Misc.LogOpenCascadeTagsRefCounting;
+
+ empty = htable_geometries_is_empty(&dev->allgeom);
+ log_type = empty ? LOG_OUTPUT : LOG_WARNING;
+ log = (option == Scad_log_all) || (!empty && option == Scad_log_only_undeleted);
+ if(log) {
+ logger_print(dev->logger, log_type,
+ "Finalizing scad; undeleted tags will be automatically unregistered.\n");
+ }
+
+ device_release(g_device);
+ g_device = NULL;
+ gmshFinalize(&ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+#define SET_GMSH_OPTION_INT(Option) \
+ gmshOptionSetNumber((#Option), (actual_options->Option), &ierr);\
+ if(ierr) {\
+ log_error(dev, "Could not set option %s to %d.\n",\
+ (#Option), (actual_options->Option));\
+ res = RES_BAD_ARG;\
+ goto error;\
+ }
+
+#define SET_GMSH_OPTION_DOUBLE(Option) \
+ gmshOptionSetNumber((#Option), (actual_options->Option), &ierr);\
+ if(ierr) {\
+ log_error(dev, "Could not set option %s to %g.\n",\
+ (#Option), (actual_options->Option));\
+ res = RES_BAD_ARG;\
+ goto error;\
+ }
+
+res_T
+scad_set_options
+ (const struct scad_options* options)
+{
+ res_T res = RES_OK;
+ const struct scad_options* actual_options
+ = options ? options : &SCAD_DEFAULT_OPTIONS;
+ int ierr = 0;
+ struct scad_options keep;
+ struct scad_device* dev = NULL;
+
+ ERR(check_device(FUNC_NAME));
+
+ dev = get_device();
+ keep = dev->options;
+
+ SET_GMSH_OPTION_INT(Mesh.Algorithm);
+ SET_GMSH_OPTION_INT(Mesh.MeshSizeExtendFromBoundary);
+ SET_GMSH_OPTION_DOUBLE(Mesh.MeshSizeFactor);
+ SET_GMSH_OPTION_DOUBLE(Mesh.MeshSizeFromCurvature);
+ SET_GMSH_OPTION_INT(Mesh.MeshSizeFromPoints);
+ SET_GMSH_OPTION_DOUBLE(Mesh.MeshSizeMax);
+ SET_GMSH_OPTION_DOUBLE(Mesh.MeshSizeMin);
+ SET_GMSH_OPTION_DOUBLE(Mesh.Smoothing);
+ SET_GMSH_OPTION_INT(Mesh.StlOneSolidPerSurface);
+
+ SET_GMSH_OPTION_INT(General.Verbosity);
+ SET_GMSH_OPTION_INT(General.ExpertMode);
+
+ SET_GMSH_OPTION_INT(Geometry.OCCParallel);
+
+ if(options) {
+ /* Check non-gmsh option validity if user-provided */
+ (void)actual_options->Misc.Step; /* int boolean: always OK */
+ (void)actual_options->Misc.SynchronizeOnRunUI; /* int boolean: always OK */
+ (void)actual_options->Misc.LogOpenCascadeTagsRefCounting; /* int boolean: always OK */
+ (void)actual_options->Misc.DebugOpenCascadeSync; /* int boolean: always OK */
+ }
+
+ dev->options = *actual_options;
+
+exit:
+ return res;
+error:
+ if(dev) dev->options = keep;
+ goto exit;
+}
+
+#undef SET_GMSH_OPTION_DOUBLE
diff --git a/src/scad_device.h b/src/scad_device.h
@@ -0,0 +1,158 @@
+/* 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/>. */
+
+#ifndef SCAD_DEVICE_H
+#define SCAD_DEVICE_H
+
+#include "scad.h"
+#include "scad_geometry.h"
+
+#include <rsys/rsys.h>
+#include <rsys/ref_count.h>
+#include <rsys/logger.h>
+#include <rsys/hash_table.h>
+#include <rsys/str.h>
+
+static INLINE char
+eq_str(const struct str* a, const struct str* b)
+{
+ return !strcmp(str_cget(a), str_cget(b));
+}
+
+static INLINE size_t
+hash_str(const struct str* a)
+{
+ return hash_fnv32(str_cget(a), str_len(a));
+}
+
+#define HTABLE_NAME names
+#define HTABLE_DATA struct scad_geometry*
+#define HTABLE_KEY struct str
+#define HTABLE_KEY_FUNCTOR_INIT str_init
+#define HTABLE_KEY_FUNCTOR_RELEASE str_release
+#define HTABLE_KEY_FUNCTOR_COPY str_copy
+#define HTABLE_KEY_FUNCTOR_COPY_AND_RELEASE str_copy_and_release
+#define HTABLE_KEY_FUNCTOR_EQ eq_str
+#define HTABLE_KEY_FUNCTOR_HASH hash_str
+#include <rsys/hash_table.h>
+
+#define HTABLE_NAME geometries
+#define HTABLE_DATA char
+#define HTABLE_KEY struct scad_geometry*
+#include <rsys/hash_table.h>
+
+#define HTABLE_NAME tags2geom
+#define HTABLE_DATA struct htable_geometries
+#define HTABLE_KEY int
+#define HTABLE_DATA_FUNCTOR_INIT htable_geometries_init
+#define HTABLE_DATA_FUNCTOR_RELEASE htable_geometries_release
+#define HTABLE_DATA_FUNCTOR_COPY htable_geometries_copy
+#define HTABLE_DATA_FUNCTOR_COPY_AND_RELEASE htable_geometries_copy_and_release
+#include <rsys/hash_table.h>
+
+struct scad_device {
+ struct logger* logger;
+ struct mem_allocator* allocator;
+ struct scad_options options;
+ struct htable_names geometry_names;
+ struct htable_geometries allgeom;
+ struct htable_tags2geom tags2geom[2];
+ int verbose;
+ int need_synchro;
+
+ ref_T ref;
+};
+
+/*******************************************************************************
+ * Helper functions
+ ******************************************************************************/
+/* Conditionally log a message on the LOG_ERROR stream of the device logger,
+ * with respect to the device verbose flag */
+extern LOCAL_SYM void
+log_error
+ (struct scad_device* dev,
+ const char* msg,
+ ...)
+#ifdef COMPILER_GCC
+ __attribute((format(printf, 2, 3)))
+#endif
+;
+
+/* Conditionally log a message on the LOG_WARNING stream of the device logger,
+ * with respect to the device verbose flag */
+extern LOCAL_SYM void
+log_warning
+ (struct scad_device* dev,
+ const char* msg,
+ ...)
+#ifdef COMPILER_GCC
+ __attribute((format(printf, 2, 3)))
+#endif
+;
+
+/* Conditionally log a message on the LOG_OUTPUT stream of the device logger,
+ * with respect to the device verbose flag */
+extern LOCAL_SYM void
+log_message
+ (struct scad_device* dev,
+ const char* msg,
+ ...)
+#ifdef COMPILER_GCC
+ __attribute((format(printf, 2, 3)))
+#endif
+;
+
+static INLINE void
+log_msg
+ (struct scad_device* dev,
+ const enum log_type stream,
+ const char* msg,
+ va_list vargs)
+{
+ res_T res; (void)res;
+ ASSERT(dev && msg);
+ res = logger_vprint(dev->logger, stream, msg, vargs);
+ ASSERT(res == RES_OK);
+}
+
+/*******************************************************************************
+ * Exported scad_device functions
+ ******************************************************************************/
+LOCAL_SYM res_T
+check_device
+ (const char* function_name);
+
+LOCAL_SYM struct scad_device*
+get_device
+ (void);
+
+LOCAL_SYM res_T
+device_register_tags
+ (struct scad_geometry* geom);
+
+LOCAL_SYM res_T
+device_unregister_tags
+ (const int log,
+ const enum log_type log_type,
+ struct scad_geometry* geom);
+
+LOCAL_SYM res_T
+device_apply_mappings
+ (const int* original_dimTags,
+ int** mappings,
+ size_t* mappings_counts, /* Number of items in each mapping */
+ const size_t mappings_count); /* Number of mappings; count(original_dimTags)/2 */
+
+#endif
diff --git a/src/scad_geometry.c b/src/scad_geometry.c
@@ -0,0 +1,1960 @@
+/* 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/>. */
+
+#include "rsys/logger.h"
+#include "scad.h"
+#include "scad_c.h"
+#include "scad_device.h"
+#include "scad_geometry.h"
+
+#include <rsys/rsys.h>
+#include <rsys/mem_allocator.h>
+#include <rsys/str.h>
+#include <rsys/math.h>
+#include <rsys/double3.h>
+
+#include <rsys/hash_table.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <gmsh/gmshc.h>
+
+/* A type used to deduplicate tags */
+#define HTABLE_NAME tags
+#define HTABLE_KEY int
+#define HTABLE_DATA char
+#include <rsys/hash_table.h>
+
+/* A type used for mappings */
+#define HTABLE_NAME mappings
+#define HTABLE_KEY int
+#define HTABLE_DATA size_t
+#include <rsys/hash_table.h>
+
+/*******************************************************************************
+ * Utility functions
+ ******************************************************************************/
+struct coord_pair {
+ const double* x;
+ const double* y;
+};
+
+static res_T
+geom_set_name
+ (struct scad_geometry* geom,
+ const char* name)
+{
+ struct scad_device* dev = get_device();
+ struct mem_allocator* allocator = dev->allocator;
+ struct str str_name;
+ int name_initialized = 0;
+ int same_name;
+ res_T res = RES_OK;
+
+ ASSERT(geom);
+
+ if(name) {
+ if(strlen(name) == 0) {
+ res = RES_BAD_ARG;
+ log_error(get_device(), "Geometry name \"\" is invalid.\n");
+ goto error;
+ }
+ str_init(allocator, &str_name);
+ name_initialized = 1;
+ ERR(str_set(&str_name, name));
+ if(htable_names_find(&dev->geometry_names, &str_name)) {
+ /* if defined, names must be unique */
+ res = RES_BAD_ARG;
+ log_error(get_device(), "Geometry name '%s' is allready in use.\n",
+ name);
+ goto error;
+ }
+ }
+
+ same_name = (!name && str_is_empty(&geom->name))
+ || (name && 0 == strcmp(name, str_cget(&geom->name)));
+
+ if(!same_name) {
+ size_t n = htable_names_erase(&dev->geometry_names, &geom->name);
+ ASSERT((n == 1) == !str_is_empty(&geom->name)); (void)n;
+ }
+
+ if(name) {
+ str_set(&geom->name, name);
+ ERR(htable_names_set(&dev->geometry_names, &geom->name, &geom));
+ } else {
+ str_clear(&geom->name);
+ }
+
+exit:
+ if(name_initialized) str_release(&str_name);
+ return res;
+error:
+ goto exit;
+}
+
+static res_T
+scad_geometry_create
+ (const char* name,
+ struct scad_geometry** out_geometry)
+{
+ res_T res = RES_OK;
+ struct scad_geometry* geom = NULL;
+ struct scad_device* dev = get_device();
+ struct mem_allocator* allocator = dev->allocator;
+ char one = 1;
+ int log;
+ enum scad_log_refcounting option;
+
+ ASSERT(out_geometry);
+ option = dev->options.Misc.LogOpenCascadeTagsRefCounting;
+ log = option == Scad_log_all;
+
+ geom = (struct scad_geometry*)MEM_CALLOC(allocator, 1, sizeof(*geom));
+ if(!geom) {
+ res = RES_MEM_ERR;
+ goto error;
+ }
+
+ str_init(allocator, &geom->name);
+ ERR(htable_geometries_set(&dev->allgeom, &geom, &one));
+ ERR(geom_set_name(geom, name));
+ dev->need_synchro = 1;
+
+end:
+ *out_geometry = geom;
+ return res;
+error:
+ if(geom) {
+ CHK(RES_OK == geometry_release(log, LOG_OUTPUT, geom));
+ geom = NULL;
+ }
+ goto end;
+}
+
+static res_T
+gather_tags
+ (struct scad_geometry** geometries,
+ const size_t geometries_count,
+ int** out_dimTags,
+ size_t* out_dimTags_n)
+{
+ res_T res = RES_OK;
+ int* dimTags = NULL;
+ size_t i, j, c, sz;
+ struct scad_device* dev = get_device();
+ struct mem_allocator* allocator = dev->allocator;
+ struct htable_tags t2, t3;
+ struct htable_tags_iterator it, end;
+
+ ASSERT((geometries || !geometries_count) && (out_dimTags || !out_dimTags_n));
+
+ htable_tags_init(allocator, &t2);
+ htable_tags_init(allocator, &t3);
+
+ /* list tags and remove duplicates */
+ for(i = 0; i < geometries_count; i++) {
+ for(j = 0; j < geometries[i]->gmsh_dimTags_n; j += 2) {
+ char one = 1;
+ int dim = geometries[i]->gmsh_dimTags[j];
+ int tag = geometries[i]->gmsh_dimTags[j+1];
+ struct htable_tags* tn = (dim == 2) ? &t2 : &t3;
+ ASSERT(dim == 2 || dim == 3);
+ ERR(htable_tags_set(tn, &tag, &one));
+ }
+ }
+
+ /* Build result */
+ sz = htable_tags_size_get(&t2) + htable_tags_size_get(&t3);
+ dimTags = MEM_ALLOC(allocator, sz * 2 * sizeof(*dimTags));
+ if(!dimTags) {
+ res = RES_MEM_ERR;
+ goto error;
+ }
+
+ c = 0;
+ htable_tags_begin(&t2, &it);
+ htable_tags_end(&t2, &end);
+ while(!htable_tags_iterator_eq(&it, &end)) {
+ dimTags[c++] = 2;
+ dimTags[c++] = *htable_tags_iterator_key_get(&it);
+ htable_tags_iterator_next(&it);
+ }
+ htable_tags_begin(&t3, &it);
+ htable_tags_end(&t3, &end);
+ while(!htable_tags_iterator_eq(&it, &end)) {
+ dimTags[c++] = 3;
+ dimTags[c++] = *htable_tags_iterator_key_get(&it);
+ htable_tags_iterator_next(&it);
+ }
+ ASSERT(sz*2 == c);
+
+ *out_dimTags_n = c;
+ *out_dimTags = dimTags;
+
+exit:
+ htable_tags_release(&t2);
+ htable_tags_release(&t3);
+ return res;
+error:
+ MEM_RM(allocator, dimTags);
+ goto exit;
+}
+
+/* gmsh documentation states that memory allocated by gmsh should be freed using
+ * gmshFree.
+ * According to valgrind map results as allocated by gmsh are not fully freed if
+ * simply freed using gmshFree.
+ * A code review shows that maps content is recursivelly allocated.
+ * We end writting this helper function that free maps content and makes
+ * valgrind happy. */
+static void
+free_gmsh_map
+ (int** map,
+ size_t mapnn)
+{
+ size_t i;
+ if(!mapnn) return;
+ for(i = 0; i < mapnn; i++) {
+ gmshFree(map[i]);
+ }
+ gmshFree(map);
+}
+
+/*******************************************************************************
+ * Local functions
+ ******************************************************************************/
+res_T
+geometry_release
+ (const int log,
+ const enum log_type log_type,
+ struct scad_geometry* geom)
+{
+ struct scad_device* dev = get_device();
+ struct mem_allocator* allocator = dev->allocator;
+ res_T res = RES_OK;
+ size_t n;
+
+ ASSERT(geom);
+
+ dev->need_synchro = 1;
+
+ ERR(device_unregister_tags(log, log_type, geom));
+ MEM_RM(allocator, geom->gmsh_dimTags);
+ if(str_len(&geom->name) != 0) {
+ n = htable_names_erase(&dev->geometry_names, &geom->name);
+ ASSERT(n == 1);
+ }
+ str_release(&geom->name);
+ n = htable_geometries_erase(&dev->allgeom, &geom);
+ ASSERT(n == 1); (void)n;
+ MEM_RM(allocator, geom);
+
+end:
+ return res;
+error:
+ goto end;
+}
+
+/******************************************************************************
+ * Exported functions
+ *****************************************************************************/
+res_T
+scad_geometry_delete
+ (struct scad_geometry* geom)
+{
+ res_T res = RES_OK;
+ struct scad_device* dev = get_device();
+ int log;
+ enum scad_log_refcounting option;
+
+ if(!geom) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ ERR(check_device(FUNC_NAME));
+ option = dev->options.Misc.LogOpenCascadeTagsRefCounting;
+ log = option == Scad_log_all;
+
+ CHK(RES_OK == geometry_release(log, LOG_OUTPUT, geom));
+
+end:
+ return res;
+error:
+ goto end;
+}
+
+res_T
+scad_scene_clear
+ (void)
+{
+ res_T res = RES_OK;
+ int ierr;
+ struct htable_geometries tmp;
+ struct htable_geometries_iterator it, end;
+ struct scad_device* dev = get_device();
+ struct mem_allocator* allocator = NULL;
+ int log, empty;
+ enum scad_log_refcounting option;
+ enum log_type log_type;
+
+ ERR(check_device(FUNC_NAME));
+ allocator = dev->allocator;
+ option = dev->options.Misc.LogOpenCascadeTagsRefCounting;
+
+ htable_geometries_init(allocator, &tmp);
+ ERR(htable_geometries_copy(&tmp, &dev->allgeom));
+ htable_geometries_begin(&tmp, &it);
+ htable_geometries_end(&tmp, &end);
+ empty = htable_geometries_is_empty(&dev->allgeom);
+ log_type = empty ? LOG_OUTPUT : LOG_WARNING;
+ log = (option == Scad_log_all) || (!empty && option == Scad_log_only_undeleted);
+ if(log) {
+ logger_print(dev->logger, log_type, "Clearing scene.\n");
+ if(empty) {
+ logger_print(dev->logger, log_type, "scene is empty.\n");
+ }
+ }
+ while(!htable_geometries_iterator_eq(&it, &end)) {
+ struct scad_geometry* geom = *htable_geometries_iterator_key_get(&it);
+ CHK(RES_OK == geometry_release(log, log_type, geom));
+ htable_geometries_iterator_next(&it);
+ }
+ if(log) {
+ logger_print(dev->logger, log_type, "End clearing scene.\n");
+ }
+
+ /* Ensure clear is complete (not scad-registered tags) */
+ gmshClear(&ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+
+end:
+ if(allocator) htable_geometries_release(&tmp);
+ return res;
+error:
+ goto end;
+}
+
+res_T
+scad_geometry_get_count
+ (const struct scad_geometry* geom,
+ size_t* count)
+{
+ res_T res = RES_OK;
+
+ if(!geom || !count) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ ERR(check_device(FUNC_NAME));
+
+ ASSERT(geom->gmsh_dimTags_n % 2 == 0);
+ *count = geom->gmsh_dimTags_n / 2;
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+res_T
+scad_geometry_get_name
+ (const struct scad_geometry* geom,
+ const char** name)
+{
+ res_T res = RES_OK;
+
+ if(!geom || !name) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ ERR(check_device(FUNC_NAME));
+
+ *name = str_cget(&geom->name);
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+res_T
+scad_geometry_swap_names
+ (struct scad_geometry* geom1,
+ struct scad_geometry* geom2)
+{
+ res_T res = RES_OK;
+ int init = 0;
+ struct str tmp;
+ struct scad_device* dev = get_device();
+ struct mem_allocator* allocator = NULL;
+
+ if(!geom1 || !geom2) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ ERR(check_device(FUNC_NAME));
+ allocator = dev->allocator;
+
+ if(!str_is_empty(&geom1->name)) {
+ ERR(htable_names_set(&dev->geometry_names, &geom1->name, &geom2));
+ }
+ if(!str_is_empty(&geom2->name)) {
+ ERR(htable_names_set(&dev->geometry_names, &geom2->name, &geom1));
+ }
+
+ str_init(allocator, & tmp);
+ init = 1;
+ ERR(str_copy(&tmp, &geom1->name));
+ ERR(str_copy(&geom1->name, &geom2->name));
+ ERR(str_copy(&geom2->name, &tmp));
+
+exit:
+ if(init) str_release(&tmp);
+ return res;
+error:
+ goto exit;
+}
+
+res_T
+scad_geometry_get_mass
+ (struct scad_geometry* geom,
+ double* mass)
+{
+ res_T res = RES_OK;
+ int dim = 0;
+ size_t i, count = 0;
+ int* data = NULL;
+ size_t sz = 0;
+ struct scad_device* dev = get_device();
+ struct mem_allocator* allocator = NULL;
+
+ if(!geom || !mass) goto error;
+
+ ERR(check_device(FUNC_NAME));
+ allocator = dev->allocator;
+
+ ASSERT(geom->gmsh_dimTags_n % 2 == 0);
+ count = geom->gmsh_dimTags_n / 2;
+
+ ERR(gather_tags(&geom, 1, &data, &sz));
+
+ dim = data[0];
+ *mass = 0;
+ for(i=0; i<count; ++i) {
+ double geom_mass = 0;
+ int ierr = 0;
+ if(data[2*i] != dim) goto error;
+ gmshModelOccGetMass(data[2*i], data[2*i + 1], &geom_mass, &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+ *mass += geom_mass;
+ }
+
+exit:
+ if(allocator) MEM_RM(allocator, data);
+ return res;
+error:
+ goto exit;
+}
+
+res_T
+scad_geometry_get_centerofmass
+ (struct scad_geometry* geom,
+ double* center)
+{
+ res_T res = RES_OK;
+ size_t i = 0;
+
+ if(!geom || !center) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ ERR(check_device(FUNC_NAME));
+
+ for(i = 0; i < geom->gmsh_dimTags_n; i += 2) {
+ double x, y, z;
+ int ierr = 0;
+ int dim = geom->gmsh_dimTags[i];
+ int tag = geom->gmsh_dimTags[i + 1];
+ gmshModelOccGetCenterOfMass(dim, tag, &x, &y, &z, &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+ center[3*i] = x;
+ center[3*i + 1] = y;
+ center[3*i + 2] = z;
+ }
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+res_T
+scad_add_rectangle
+ (const char* name,
+ const double xyz[3],
+ const double dxdy[2],
+ struct scad_geometry** out_geometry)
+{
+ res_T res = RES_OK;
+ int ierr, gmsh_ID;
+ struct scad_geometry* geom = NULL;
+ struct scad_device* dev = get_device();
+ struct mem_allocator* allocator = NULL;
+ int log;
+ enum scad_log_refcounting option;
+
+ if(!xyz || !dxdy || !out_geometry) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ ERR(check_device(FUNC_NAME));
+ allocator = dev->allocator;
+ option = dev->options.Misc.LogOpenCascadeTagsRefCounting;
+ log = option == Scad_log_all;
+
+ gmsh_ID = gmshModelOccAddRectangle(SPLIT3(xyz), SPLIT2(dxdy), -1, 0, &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+
+ ERR(scad_geometry_create(name, &geom));
+ geom->gmsh_dimTags_n = 2;
+ geom->gmsh_dimTags
+ = MEM_ALLOC(allocator, geom->gmsh_dimTags_n * sizeof(*geom->gmsh_dimTags));
+ if(!geom->gmsh_dimTags) {
+ res = RES_MEM_ERR;
+ goto error;
+ }
+ geom->gmsh_dimTags[0] = 2;
+ geom->gmsh_dimTags[1] = gmsh_ID;
+
+ ERR(device_register_tags(geom));
+
+exit:
+ if(out_geometry) *out_geometry = geom;
+ return res;
+error:
+ if(geom) {
+ CHK(RES_OK == geometry_release(log, LOG_OUTPUT, geom));
+ geom = NULL;
+ }
+ goto exit;
+}
+
+res_T
+scad_add_disk
+ (const char* name,
+ const double xyz[3],
+ const double radius,
+ struct scad_geometry** out_geometry)
+{
+ res_T res = RES_OK;
+ int ierr, gmsh_ID;
+ struct scad_geometry* geom = NULL;
+ struct scad_device* dev = get_device();
+ struct mem_allocator* allocator = NULL;
+ int log;
+ enum scad_log_refcounting option;
+
+ if(!xyz || radius <= 0 || !out_geometry) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ ERR(check_device(FUNC_NAME));
+ allocator = dev->allocator;
+ option = dev->options.Misc.LogOpenCascadeTagsRefCounting;
+ log = option == Scad_log_all;
+
+ gmsh_ID = gmshModelOccAddDisk(SPLIT3(xyz), radius, radius, -1, &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+
+ ERR(scad_geometry_create(name, &geom));
+ geom->gmsh_dimTags_n = 2;
+ geom->gmsh_dimTags
+ = MEM_ALLOC(allocator, geom->gmsh_dimTags_n * sizeof(*geom->gmsh_dimTags));
+ if(!geom->gmsh_dimTags) {
+ res = RES_MEM_ERR;
+ goto error;
+ }
+ geom->gmsh_dimTags[0] = 2;
+ geom->gmsh_dimTags[1] = gmsh_ID;
+
+ ERR(device_register_tags(geom));
+
+exit:
+ if(out_geometry) *out_geometry = geom;
+ return res;
+error:
+ if(geom) {
+ CHK(RES_OK == geometry_release(log, LOG_OUTPUT, geom));
+ geom = NULL;
+ }
+ goto exit;
+}
+
+res_T
+scad_add_polygon
+ (const char* name,
+ void (*get_position)(const size_t ivert, double pos[2], void* data),
+ void* data,
+ const double z,
+ const size_t count,
+ struct scad_geometry** out_geometry)
+{
+ res_T res = RES_OK;
+ int ierr, gmsh_ID;
+ struct scad_geometry* geom = NULL;
+ size_t i;
+ int* points = NULL;
+ int* lines = NULL;
+ int loop;
+ struct scad_device* dev = get_device();
+ struct mem_allocator* allocator = NULL;
+ int log;
+ enum scad_log_refcounting option;
+
+ if(!get_position || count < 3 || !out_geometry) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ ERR(check_device(FUNC_NAME));
+ allocator = dev->allocator;
+ option = dev->options.Misc.LogOpenCascadeTagsRefCounting;
+ log = option == Scad_log_all;
+
+ points = MEM_ALLOC(allocator, count * sizeof(*points));
+ lines = MEM_ALLOC(allocator, count * sizeof(*lines));
+ if(!points || !lines) {
+ res = RES_MEM_ERR;
+ goto error;
+ }
+
+ for(i=0; i<count; ++i) {
+ double pos[2];
+ get_position(i, pos, data);
+ points[i] = gmshModelOccAddPoint(pos[0], pos[1], z, -1, -1, &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+ }
+
+ for(i=0; i<count; i++) {
+ size_t end = (i == count-1) ? 0 : i+1;
+ lines[i] = gmshModelOccAddLine(points[i], points[end], -1, &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+ }
+
+ loop = gmshModelOccAddCurveLoop(lines, count, -1, &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+
+ gmsh_ID = gmshModelOccAddPlaneSurface(&loop, 1, -1, &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+
+ ERR(scad_geometry_create(name, &geom));
+ geom->gmsh_dimTags_n = 2;
+ geom->gmsh_dimTags = MEM_ALLOC(allocator, 2 * sizeof(*geom->gmsh_dimTags));
+ if(!geom->gmsh_dimTags) {
+ res = RES_MEM_ERR;
+ goto error;
+ }
+ geom->gmsh_dimTags[0] = 2;
+ geom->gmsh_dimTags[1] = gmsh_ID;
+
+ ERR(device_register_tags(geom));
+
+exit:
+ if(out_geometry) *out_geometry = geom;
+ if(allocator) {
+ MEM_RM(allocator, points);
+ MEM_RM(allocator, lines);
+ }
+ return res;
+error:
+ if(geom) {
+ CHK(RES_OK == geometry_release(log, LOG_OUTPUT, geom));
+ geom = NULL;
+ }
+ goto exit;
+}
+
+res_T
+scad_add_box
+ (const char* name,
+ const double xyz[3],
+ const double dxdydz[3],
+ struct scad_geometry** out_geometry)
+{
+ res_T res = RES_OK;
+ int ierr, gmsh_ID;
+ struct scad_geometry* geom = NULL;
+ struct scad_device* dev = get_device();
+ struct mem_allocator* allocator = NULL;
+ int log;
+ enum scad_log_refcounting option;
+
+ if(!xyz || !dxdydz || !out_geometry) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ ERR(check_device(FUNC_NAME));
+ allocator = dev->allocator;
+ option = dev->options.Misc.LogOpenCascadeTagsRefCounting;
+ log = option == Scad_log_all;
+
+ gmsh_ID = gmshModelOccAddBox(SPLIT3(xyz), SPLIT3(dxdydz), -1, &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+
+ ERR(scad_geometry_create(name, &geom));
+ geom->gmsh_dimTags_n = 2;
+ geom->gmsh_dimTags = MEM_ALLOC(allocator, 2 * sizeof(*geom->gmsh_dimTags));
+ if(!geom->gmsh_dimTags) {
+ res = RES_MEM_ERR;
+ goto error;
+ }
+ geom->gmsh_dimTags[0] = 3;
+ geom->gmsh_dimTags[1] = gmsh_ID;
+
+ ERR(device_register_tags(geom));
+
+exit:
+ if(out_geometry) *out_geometry = geom;
+ return res;
+error:
+ if(geom) {
+ CHK(RES_OK == geometry_release(log, LOG_OUTPUT, geom));
+ geom = NULL;
+ }
+ goto exit;
+}
+
+res_T
+scad_add_cylinder
+ (const char* name,
+ const double xyz[3],
+ const double axis[3],
+ const double radius,
+ const double angle,
+ struct scad_geometry** out_geometry)
+{
+ res_T res = RES_OK;
+ int ierr, gmsh_ID;
+ struct scad_geometry* geom = NULL;
+ struct scad_device* dev = get_device();
+ struct mem_allocator* allocator = NULL;
+ int log;
+ enum scad_log_refcounting option;
+
+ if(!xyz || !axis || radius <= 0 || angle < 0 || angle > 2*PI || !out_geometry) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ ERR(check_device(FUNC_NAME));
+ allocator = dev->allocator;
+ option = dev->options.Misc.LogOpenCascadeTagsRefCounting;
+ log = option == Scad_log_all;
+
+ gmsh_ID = gmshModelOccAddCylinder(SPLIT3(xyz), SPLIT3(axis), radius, -1,
+ angle, &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+
+ ERR(scad_geometry_create(name, &geom));
+ geom->gmsh_dimTags_n = 2;
+ geom->gmsh_dimTags = MEM_ALLOC(allocator, 2 * sizeof(*geom->gmsh_dimTags));
+ if(!geom->gmsh_dimTags) {
+ res = RES_MEM_ERR;
+ goto error;
+ }
+ geom->gmsh_dimTags[0] = 3;
+ geom->gmsh_dimTags[1] = gmsh_ID;
+
+ ERR(device_register_tags(geom));
+
+exit:
+ if(out_geometry) *out_geometry = geom;
+ return res;
+error:
+ if(geom) {
+ CHK(RES_OK == geometry_release(log, LOG_OUTPUT, geom));
+ geom = NULL;
+ }
+ goto exit;
+}
+
+int
+scad_add_sphere
+ (const char* name,
+ const double xyz[3],
+ const double radius,
+ struct scad_geometry** out_geometry)
+{
+ res_T res = RES_OK;
+ int ierr, gmsh_ID;
+ struct scad_geometry* geom = NULL;
+ struct scad_device* dev = get_device();
+ struct mem_allocator* allocator = NULL;
+ int log;
+ enum scad_log_refcounting option;
+
+ if(!xyz || radius <= 0 || !out_geometry) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ ERR(check_device(FUNC_NAME));
+ allocator = dev->allocator;
+ option = dev->options.Misc.LogOpenCascadeTagsRefCounting;
+ log = option == Scad_log_all;
+
+ gmsh_ID =
+ gmshModelOccAddSphere(SPLIT3(xyz), radius, -1, -PI/2, PI/2, 2*PI, &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+
+ ERR(scad_geometry_create(name, &geom));
+ geom->gmsh_dimTags_n = 2;
+ geom->gmsh_dimTags = MEM_ALLOC(allocator, 2 * sizeof(*geom->gmsh_dimTags));
+ if(!geom->gmsh_dimTags) {
+ res = RES_MEM_ERR;
+ goto error;
+ }
+ geom->gmsh_dimTags[0] = 3;
+ geom->gmsh_dimTags[1] = gmsh_ID;
+
+ ERR(device_register_tags(geom));
+
+exit:
+ if(out_geometry) *out_geometry = geom;
+ return res;
+error:
+ if(geom) {
+ CHK(RES_OK == geometry_release(log, LOG_OUTPUT, geom));
+ geom = NULL;
+ }
+ goto exit;
+}
+
+res_T
+scad_fuse_geometries
+ (const char* name,
+ struct scad_geometry** geometries,
+ const size_t geometries_count,
+ struct scad_geometry** tools,
+ const size_t tools_count,
+ struct scad_geometry** out_geometry)
+{
+ res_T res = RES_OK;
+ int* tagout = NULL;
+ int** map = NULL;
+ size_t* mapn = NULL;
+ size_t tagoutn, mapnn = 0, sz1, sz2;
+ int* data1 = NULL;
+ int* data2 = NULL;
+ int ierr = 0;
+ struct scad_geometry* geom = NULL;
+ struct scad_device* dev = get_device();
+ struct mem_allocator* allocator = NULL;
+ int log;
+ enum scad_log_refcounting option;
+
+ if(!geometries || !geometries_count || !tools || !tools_count || !out_geometry) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ ERR(check_device(FUNC_NAME));
+ allocator = dev->allocator;
+ option = dev->options.Misc.LogOpenCascadeTagsRefCounting;
+ log = option == Scad_log_all;
+
+ ERR(gather_tags(geometries, geometries_count, &data1, &sz1));
+ ERR(gather_tags(tools, tools_count, &data2, &sz2));
+
+ /* We don't remove gmsh objects here; they are only removed when their tags are
+ * no longuer used by any star-cad geometry */
+ gmshModelOccFuse(data1, sz1, data2, sz2, &tagout, &tagoutn, &map, &mapn,
+ &mapnn, -1, 0, 0, &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+
+ ERR(scad_geometry_create(name, &geom));
+ geom->gmsh_dimTags_n = tagoutn;
+ geom->gmsh_dimTags = MEM_ALLOC(allocator, tagoutn * sizeof(*tagout));
+ if(!geom->gmsh_dimTags) {
+ res = RES_MEM_ERR;
+ goto error;
+ }
+ memcpy(geom->gmsh_dimTags, tagout, tagoutn * sizeof(*tagout));
+
+ ERR(device_register_tags(geom));
+
+exit:
+ if(out_geometry) *out_geometry = geom;
+ if(allocator) {
+ MEM_RM(allocator, data1);
+ MEM_RM(allocator, data2);
+ }
+ gmshFree(mapn);
+ gmshFree(tagout);
+ free_gmsh_map(map, mapnn);
+ return res;
+error:
+ if(geom) {
+ CHK(RES_OK == geometry_release(log, LOG_OUTPUT, geom));
+ geom = NULL;
+ }
+ if(tagout) gmshModelOccRemove(tagout, tagoutn, 1, &ierr);
+ goto exit;
+}
+
+res_T
+scad_cut_geometries
+ (const char* name, /* Can be NULL */
+ struct scad_geometry** geometries,
+ const size_t geometries_count,
+ struct scad_geometry** tools,
+ const size_t tools_count,
+ struct scad_geometry** out_geometry)
+{
+ res_T res = RES_OK;
+ int* tagout = NULL;
+ int** map = NULL;
+ size_t* mapn = NULL;
+ size_t tagoutn, mapnn = 0, sz1, sz2;
+ int* data1 = NULL;
+ int* data2 = NULL;
+ int ierr = 0;
+ struct scad_geometry* geom = NULL;
+ struct scad_device* dev = get_device();
+ struct mem_allocator* allocator = NULL;
+ int log;
+ enum scad_log_refcounting option;
+
+ if(!geometries || !geometries_count || !tools || !tools_count || !out_geometry) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ ERR(check_device(FUNC_NAME));
+ allocator = dev->allocator;
+ option = dev->options.Misc.LogOpenCascadeTagsRefCounting;
+ log = option == Scad_log_all;
+
+ ERR(gather_tags(geometries, geometries_count, &data1, &sz1));
+ ERR(gather_tags(tools, tools_count, &data2, &sz2));
+
+ /* We don't remove gmsh objects here; they are only removed when their tags are
+ * no longuer used by any star-cad geometry */
+ gmshModelOccCut(data1, sz1, data2, sz2, &tagout, &tagoutn, &map, &mapn,
+ &mapnn, -1, 0, 0, &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+
+ ERR(scad_geometry_create(name, &geom));
+ geom->gmsh_dimTags_n = tagoutn;
+ geom->gmsh_dimTags = MEM_ALLOC(allocator, tagoutn * sizeof(*tagout));
+ if(!geom->gmsh_dimTags) {
+ res = RES_MEM_ERR;
+ goto error;
+ }
+ memcpy(geom->gmsh_dimTags, tagout, tagoutn * sizeof(*tagout));
+
+ ERR(device_register_tags(geom));
+
+exit:
+ if(out_geometry) *out_geometry = geom;
+ if(allocator) {
+ MEM_RM(allocator, data1);
+ MEM_RM(allocator, data2);
+ }
+ gmshFree(mapn);
+ gmshFree(tagout);
+ free_gmsh_map(map, mapnn);
+ return res;
+error:
+ if(geom) {
+ CHK(RES_OK == geometry_release(log, LOG_OUTPUT, geom));
+ geom = NULL;
+ }
+ if(tagout) gmshModelOccRemove(tagout, tagoutn, 1, &ierr);
+ goto exit;
+}
+
+res_T
+scad_intersect_geometries
+ (const char* name, /* Can be NULL */
+ struct scad_geometry** geometries,
+ const size_t geometries_count,
+ struct scad_geometry** tools,
+ const size_t tools_count,
+ struct scad_geometry** out_geometry)
+{
+ res_T res = RES_OK;
+ int* tagout = NULL;
+ int** map = NULL;
+ size_t* mapn = NULL;
+ size_t tagoutn, mapnn = 0, sz1, sz2;
+ int* data1 = NULL;
+ int* data2 = NULL;
+ int ierr = 0;
+ struct scad_geometry* geom = NULL;
+ struct scad_device* dev = get_device();
+ struct mem_allocator* allocator = NULL;
+ int log;
+ enum scad_log_refcounting option;
+
+ if(!geometries || !geometries_count || !tools || !tools_count || !out_geometry) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ ERR(check_device(FUNC_NAME));
+ allocator = dev->allocator;
+ option = dev->options.Misc.LogOpenCascadeTagsRefCounting;
+ log = option == Scad_log_all;
+
+ ERR(gather_tags(geometries, geometries_count, &data1, &sz1));
+ ERR(gather_tags(tools, tools_count, &data2, &sz2));
+
+ /* We don't remove gmsh objects here; they are only removed when their tags are
+ * no longuer used by any star-cad geometry */
+ gmshModelOccIntersect(data1, sz1, data2, sz2, &tagout, &tagoutn, &map, &mapn,
+ &mapnn, -1, 0, 0, &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+
+ ERR(scad_geometry_create(name, &geom));
+ geom->gmsh_dimTags_n = tagoutn;
+ if (tagoutn == 0){
+ geom->gmsh_dimTags = NULL;
+ } else {
+ geom->gmsh_dimTags = MEM_ALLOC(allocator, tagoutn * sizeof(*tagout));
+ if(!geom->gmsh_dimTags) {
+ res = RES_MEM_ERR;
+ goto error;
+ }
+ memcpy(geom->gmsh_dimTags, tagout, tagoutn * sizeof(*tagout));
+ }
+
+ ERR(device_register_tags(geom));
+
+exit:
+ if(out_geometry) *out_geometry = geom;
+ if(allocator) {
+ MEM_RM(allocator, data1);
+ MEM_RM(allocator, data2);
+ }
+ gmshFree(mapn);
+ gmshFree(tagout);
+ free_gmsh_map(map, mapnn);
+ return res;
+error:
+ if(geom) {
+ CHK(RES_OK == geometry_release(log, LOG_OUTPUT, geom));
+ geom = NULL;
+ }
+ if(tagout) gmshModelOccRemove(tagout, tagoutn, 1, &ierr);
+ goto exit;
+}
+
+res_T
+scad_geometries_common_boundaries
+ (const char* name, /* Can be NULL */
+ struct scad_geometry** geometries,
+ const size_t geometries_count,
+ struct scad_geometry** tools,
+ const size_t tools_count,
+ struct scad_geometry** out_geometry)
+{
+ res_T res = RES_OK;
+ int* tagout = NULL;
+ int** map = NULL;
+ size_t* mapn = NULL;
+ size_t tagoutn, mapnn = 0, sz1, sz2;
+ int* data1 = NULL;
+ int* data2 = NULL;
+ int ierr = 0;
+ int* bound1 = NULL;
+ int* bound2 = NULL;
+ size_t n1, n2;
+ struct scad_geometry* geom = NULL;
+ struct mem_allocator* allocator = NULL;
+ struct scad_device* dev = get_device();
+ int log;
+ enum scad_log_refcounting option;
+
+ if(!geometries || !geometries_count || !tools || !tools_count || !out_geometry) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ ERR(check_device(FUNC_NAME));
+ allocator = dev->allocator;
+ option = dev->options.Misc.LogOpenCascadeTagsRefCounting;
+ log = option == Scad_log_all;
+
+ ERR(gather_tags(geometries, geometries_count, &data1, &sz1));
+ ERR(gather_tags(tools, tools_count, &data2, &sz2));
+
+ /* We don't remove gmsh objects here; they are only removed when their tags are
+ * no longuer used by any star-cad geometry */
+ gmshModelGetBoundary(data1, sz1, &bound1, &n1, 1, 0, 0, &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+ gmshModelGetBoundary(data2, sz2, &bound2, &n2, 1, 0, 0, &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+ gmshModelOccIntersect(bound1, n1, bound2, n2, &tagout, &tagoutn, &map,
+ &mapn, &mapnn, -1, 0/*no delete*/, 0/*no delete*/, &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+
+ ERR(scad_geometry_create(name, &geom));
+ geom->gmsh_dimTags_n = tagoutn;
+ if (tagoutn == 0) {
+ geom->gmsh_dimTags = NULL;
+ } else {
+ geom->gmsh_dimTags = MEM_ALLOC(allocator, tagoutn * sizeof(*tagout));
+ if(!geom->gmsh_dimTags) {
+ res = RES_MEM_ERR;
+ goto error;
+ }
+ memcpy(geom->gmsh_dimTags, tagout, tagoutn * sizeof(*tagout));
+ }
+
+ ERR(device_register_tags(geom));
+
+exit:
+ if(out_geometry) *out_geometry = geom;
+ if(allocator) {
+ MEM_RM(allocator, data1);
+ MEM_RM(allocator, data2);
+ }
+ gmshFree(bound1);
+ gmshFree(bound2);
+ gmshFree(mapn);
+ gmshFree(tagout);
+ free_gmsh_map(map, mapnn);
+ return res;
+error:
+ if(geom) {
+ CHK(RES_OK == geometry_release(log, LOG_OUTPUT, geom));
+ geom = NULL;
+ }
+ if(tagout) gmshModelOccRemove(tagout, tagoutn, 1, &ierr);
+ goto exit;
+}
+
+res_T
+scad_geometry_rotate
+ (struct scad_geometry* geom,
+ const double pt[3],
+ const double axis[3],
+ const double angle)
+{
+ int* data;
+ size_t sz;
+ int ierr = 0;
+ res_T res = RES_OK;
+
+ if(!geom || !pt || !axis) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ ERR(check_device(FUNC_NAME));
+
+ sz = geom->gmsh_dimTags_n;
+ data = geom->gmsh_dimTags;
+ gmshModelOccRotate(data, sz, SPLIT3(pt), SPLIT3(axis), angle, &ierr);
+ get_device()->need_synchro = 1;
+ ERR(gmsh_err_to_res_T(ierr));
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+res_T
+scad_geometry_extrude
+ (const struct scad_geometry* geom,
+ const char* name,
+ const double dxdydz[3],
+ struct scad_geometry** out_geometry)
+{
+ res_T res = RES_OK;
+ int* tagout = NULL;
+ size_t tagoutn;
+ size_t i, j;
+ int* extrude_data = NULL;
+ size_t extrude_sz = 0;
+ int ierr = 0;
+ struct scad_geometry* extrude_geom = NULL;
+ struct scad_device* dev = get_device();
+ struct mem_allocator* allocator = NULL;
+ int log;
+ enum scad_log_refcounting option;
+
+ if(!geom || !dxdydz || !out_geometry) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ ERR(check_device(FUNC_NAME));
+ allocator = dev->allocator;
+ option = dev->options.Misc.LogOpenCascadeTagsRefCounting;
+ log = option == Scad_log_all;
+
+ gmshModelOccExtrude(geom->gmsh_dimTags, geom->gmsh_dimTags_n, SPLIT3(dxdydz),
+ &tagout, &tagoutn, NULL, 0, NULL, 0, 0, &ierr);
+ get_device()->need_synchro = 1;
+ ERR(gmsh_err_to_res_T(ierr));
+
+ ERR(scad_geometry_create(name, &extrude_geom));
+ /* keep only 3D entities */
+ /* TODO : NOT SURE OF THE CONCEPT */
+ for(i=0; i<tagoutn; i+=2) {
+ int dim = tagout[i];
+ if(dim == 3) extrude_sz += 2;
+ }
+ extrude_data = MEM_ALLOC(allocator, extrude_sz * sizeof(*extrude_data));
+ if(!extrude_data) {
+ res = RES_MEM_ERR;
+ goto error;
+ }
+ j = 0;
+ for(i=0; i<tagoutn; i+=2) {
+ int dim = tagout[i];
+ int tag = tagout[i+1];
+ if(dim == 3) {
+ extrude_data[j] = dim;
+ extrude_data[j+1] = tag;
+ j += 2;
+ }
+ }
+ ASSERT(j == extrude_sz);
+ extrude_geom->gmsh_dimTags_n = extrude_sz;
+ extrude_geom->gmsh_dimTags = extrude_data;
+ ERR(device_register_tags(extrude_geom));
+
+exit:
+ if(out_geometry) *out_geometry = extrude_geom;
+ gmshFree(tagout);
+ return res;
+error:
+ if(extrude_geom) {
+ CHK(RES_OK == geometry_release(log, LOG_OUTPUT, extrude_geom));
+ extrude_geom = NULL;
+ }
+ goto exit;
+}
+
+res_T
+scad_geometry_explode
+ (const struct scad_geometry* geom,
+ const char* prefix_name, /* Can be NULL */
+ struct scad_geometry*** out_geometry,
+ size_t* out_geometry_n)
+{
+ res_T res = RES_OK;
+ int* data = NULL;
+ size_t i, sz = 0;
+ struct scad_geometry** geom_array = NULL;
+ struct str name;
+ int name_initialized = 0;
+ struct scad_device* dev = get_device();
+ struct mem_allocator* allocator = NULL;
+
+ if(!geom || !out_geometry || !out_geometry_n) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ ERR(check_device(FUNC_NAME));
+ allocator = dev->allocator;
+
+ data = geom->gmsh_dimTags;
+ sz = geom->gmsh_dimTags_n;
+
+ ASSERT(sz % 2 == 0);
+ geom_array = MEM_CALLOC(allocator, sz/2, sizeof(*geom_array));
+ if(!geom_array) {
+ res = RES_MEM_ERR;
+ goto error;
+ }
+
+ if(prefix_name) {
+ str_init(allocator, &name);
+ name_initialized = 1;
+ }
+ for(i=0; i<sz/2; ++i) {
+ if(prefix_name) {
+ ERR(str_set(&name, prefix_name));
+ ERR(str_append_printf(&name,"_%lu", (unsigned long)i));
+ ERR(scad_geometry_create(str_cget(&name), geom_array+i));
+ } else {
+ ERR(scad_geometry_create(NULL, geom_array+i));
+ }
+ geom_array[i]->gmsh_dimTags_n = 2;
+ geom_array[i]->gmsh_dimTags
+ = MEM_ALLOC(allocator, 2 * sizeof(*geom_array[i]->gmsh_dimTags));
+ if(!geom_array[i]->gmsh_dimTags) {
+ res = RES_MEM_ERR;
+ goto error;
+ }
+ geom_array[i]->gmsh_dimTags[0] = data[2*i];
+ geom_array[i]->gmsh_dimTags[1] = data[2*i+1];
+
+ ERR(device_register_tags(geom_array[i]));
+ }
+
+exit:
+ if(out_geometry_n) *out_geometry_n = sz/2 ;
+ if(out_geometry) *out_geometry = geom_array;
+ if(name_initialized) str_release(&name);
+ return res;
+error:
+ if(geom_array) {
+ for(i = 0; i < sz/2; i++) {
+ if(geom_array[i]) SCAD(geometry_delete(geom_array[i]));
+ }
+ MEM_RM(allocator, geom_array);
+ geom_array = NULL;
+ }
+ goto exit;
+}
+
+
+res_T
+scad_geometry_copy
+ (const struct scad_geometry* geom,
+ const char* name, /* Can be NULL */
+ struct scad_geometry** out_geometry)
+{
+ res_T res = RES_OK;
+ int* data1;
+ int* tagout = NULL;
+ size_t sz1, tagoutn;
+ int ierr = 0;
+ struct scad_geometry* copy = NULL;
+ struct scad_device* dev = get_device();
+ struct mem_allocator* allocator = NULL;
+ int log;
+ enum scad_log_refcounting option;
+
+ if(!geom || !out_geometry) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ ERR(check_device(FUNC_NAME));
+ allocator = dev->allocator;
+ option = dev->options.Misc.LogOpenCascadeTagsRefCounting;
+ log = option == Scad_log_all;
+
+ sz1 = geom->gmsh_dimTags_n;
+ data1 = geom->gmsh_dimTags;
+ gmshModelOccCopy(data1, sz1, &tagout, &tagoutn, &ierr);
+ get_device()->need_synchro = 1;
+ ERR(gmsh_err_to_res_T(ierr));
+
+ ERR(scad_geometry_create(name, ©));
+ copy->gmsh_dimTags_n = tagoutn;
+ copy->gmsh_dimTags = MEM_ALLOC(allocator, tagoutn * sizeof(*tagout));
+ if(!copy->gmsh_dimTags) {
+ res = RES_MEM_ERR;
+ goto error;
+ }
+ memcpy(copy->gmsh_dimTags, tagout, tagoutn * sizeof(*tagout));
+
+ ERR(device_register_tags(copy));
+
+exit:
+ if(out_geometry) *out_geometry = copy;
+ gmshFree(tagout);
+ return res;
+error:
+ if(copy) {
+ CHK(RES_OK == geometry_release(log, LOG_OUTPUT, copy));
+ copy = NULL;
+ }
+ if(tagout) gmshModelOccRemove(tagout, tagoutn, 1, &ierr);
+ goto exit;
+}
+
+res_T
+scad_geometry_rename
+ (struct scad_geometry* geom,
+ const char* name) /* Can be NULL */
+{
+ res_T res = RES_OK;
+
+ if(!geom) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ ERR(check_device(FUNC_NAME));
+
+ ERR(geom_set_name(geom, name));
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+res_T
+scad_geometry_translate
+ (struct scad_geometry* geom,
+ const double dxdydz[3])
+{
+ int* data;
+ size_t sz;
+ int ierr = 0;
+ res_T res = RES_OK;
+
+ if(!geom || !dxdydz) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ ERR(check_device(FUNC_NAME));
+
+ sz = geom->gmsh_dimTags_n;
+ data = geom->gmsh_dimTags;
+ gmshModelOccTranslate(data, sz, SPLIT3(dxdydz), &ierr);
+ get_device()->need_synchro = 1;
+ ERR(gmsh_err_to_res_T(ierr));
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+res_T
+scad_geometries_partition
+ (struct scad_geometry** geometries,
+ const size_t geometries_count,
+ const int allow_overlapping,
+ struct scad_geometry** out_geometries)
+{
+ res_T res = RES_OK;
+ size_t i;
+ int* tagout = NULL;
+ int** map = NULL;
+ size_t* mapn = NULL;
+ size_t tagoutn = 0, mapnn = 0, sz;
+ int* data = NULL;
+ int ierr = 0;
+ struct scad_geometry** geoms = NULL;
+ struct htable_mappings m2, m3;
+ int hm_initialized = 0;
+ struct scad_device* dev = get_device();
+ struct htable_tags t2, t3;
+ struct htable_tags_iterator it, end;
+ int ht_initialized = 0;
+ struct mem_allocator* allocator = NULL;
+ int log;
+ enum scad_log_refcounting option;
+
+ if(!geometries || !geometries_count || (allow_overlapping && !out_geometries)) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ ERR(check_device(FUNC_NAME));
+ allocator = dev->allocator;
+ option = dev->options.Misc.LogOpenCascadeTagsRefCounting;
+ log = option == Scad_log_all;
+
+ ERR(gather_tags(geometries, geometries_count, &data, &sz));
+
+ /* As a general principle, we don't remove gmsh objects directly; they are
+ * only removed from scad_geometry_delete when their tags are no longuer used
+ * by any star-cad geometry.
+ * Here we can safely use the remove flag in the non-overlapping case, as
+ * this ends in the same tags being reused for output. */
+ gmshModelOccFragment(data, sz, NULL, 0, &tagout, &tagoutn, &map, &mapn,
+ &mapnn, -1, (allow_overlapping == 0), 0, &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+ ASSERT(sz == 2*mapnn); /* Because input tags where deduplicated */
+
+ get_device()->need_synchro = 1;
+
+ /* Check first if there was an overlapping problem */
+ if(!allow_overlapping) {
+ /* No overlapping means that each tag in geometries is translated into a
+ * single tag in map */
+ unsigned long ucount = 0;
+ for(i = 0; i < mapnn; i++) {
+ if(mapn[i] != 2) {
+ res = RES_BAD_ARG;
+ if(str_is_empty(&geometries[i]->name)) {
+ ucount++;
+ } else {
+ log_error(get_device(), "Geometry '%s' overlapping.\n",
+ str_cget(&geometries[i]->name));
+ }
+ }
+ /* Additionally, with the delete flag ON we expect the tags to remain
+ * unchanged in the non-overlapping case. */
+ ASSERT(res != RES_OK || allow_overlapping || map[i][1] == data[2*i+1]);
+ }
+ if(ucount) {
+ log_error(get_device(), "%lu unamed overlapping geometries.\n", ucount);
+ }
+ if(res != RES_OK) goto error;
+ } else {
+ /* Create htables of mappings to ease access */
+ htable_mappings_init(allocator, &m2);
+ htable_mappings_init(allocator, &m3);
+ hm_initialized = 1;
+ for(i = 0; i < sz; i += 2) {
+ int dim = data[i];
+ int tag = data[i+1];
+ size_t mapping = i/2;
+ struct htable_mappings* mn = (dim == 2) ? &m2 : &m3;
+ ASSERT(dim == 2 || dim == 3);
+ ERR(htable_mappings_set(mn, &tag, &mapping));
+ }
+
+ /* Create output geometries from mapping */
+ geoms = MEM_CALLOC(allocator, geometries_count, sizeof(*geoms));
+ if(!geoms) {
+ res = RES_MEM_ERR;
+ goto error;
+ }
+ htable_tags_init(allocator, &t2);
+ htable_tags_init(allocator, &t3);
+ ht_initialized = 1;
+ for(i = 0; i < geometries_count; i++) {
+ struct scad_geometry* geom = geometries[i];
+ size_t c, n, j;
+ int* dt = NULL;
+ /* For each tag in geometries[i] out_geometries[i] includes the mapped tags.
+ * The resulting tags need to be deduplicated though. */
+ htable_tags_clear(&t2);
+ htable_tags_clear(&t3);
+ for(j = 0; j < geom->gmsh_dimTags_n; j += 2) {
+ int dim = geom->gmsh_dimTags[j];
+ int tag = geom->gmsh_dimTags[j+1];
+ struct htable_mappings* mn = (dim == 2) ? &m2 : &m3;
+ size_t k;
+ size_t* mapping = htable_mappings_find(mn, &tag);
+ ASSERT(dim == 2 || dim == 3);
+ ASSERT(mapping && *mapping < mapnn);
+ for(k = 0; k < mapn[*mapping]; k += 2) {
+ char one = 1;
+ int d = map[*mapping][k];
+ int t = map[*mapping][k+1];
+ struct htable_tags* tn = (d == 2) ? &t2 : &t3;
+ ERR(htable_tags_set(tn, &t, &one));
+ }
+ }
+ /* Allocate result */
+ n = htable_tags_size_get(&t2) + htable_tags_size_get(&t3);
+ dt = MEM_ALLOC(allocator, sizeof(*dt) * 2 * n);
+ if(!dt) {
+ res = RES_MEM_ERR;
+ goto error;
+ }
+ /* Copy tags */
+ c = 0;
+ htable_tags_begin(&t2, &it);
+ htable_tags_end(&t2, &end);
+ while(!htable_tags_iterator_eq(&it, &end)) {
+ dt[c++] = 2;
+ dt[c++] = *htable_tags_iterator_key_get(&it);
+ htable_tags_iterator_next(&it);
+ }
+ htable_tags_begin(&t3, &it);
+ htable_tags_end(&t3, &end);
+ while(!htable_tags_iterator_eq(&it, &end)) {
+ dt[c++] = 3;
+ dt[c++] = *htable_tags_iterator_key_get(&it);
+ htable_tags_iterator_next(&it);
+ }
+ ASSERT(c == 2*n);
+
+ /* Create geometry */
+ ERR(scad_geometry_create(NULL, geoms+i));
+ geoms[i]->gmsh_dimTags_n = c;
+ geoms[i]->gmsh_dimTags = dt;
+ ERR(device_register_tags(geoms[i]));
+ }
+ memcpy(out_geometries, geoms, geometries_count * sizeof(*geoms));
+ }
+
+exit:
+ gmshFree(mapn);
+ free_gmsh_map(map, mapnn);
+ if(hm_initialized) {
+ htable_mappings_release(&m2);
+ htable_mappings_release(&m3);
+ }
+ if(ht_initialized) {
+ htable_tags_release(&t2);
+ htable_tags_release(&t3);
+ }
+ if(allocator) {
+ MEM_RM(allocator, data);
+ MEM_RM(allocator, geoms);
+ }
+ gmshFree(tagout);
+ return res;
+error:
+ if(geoms) {
+ for(i = 0; i < geometries_count; i++) {
+ if(geoms[i]) CHK(RES_OK == geometry_release(log, LOG_OUTPUT, geoms[i]));
+ }
+ }
+ if(tagout) {
+ gmshModelOccRemove(tagout, tagoutn, 1, &ierr);
+ }
+ goto exit;
+}
+
+res_T
+scad_fragment_geometries
+ (const char* name,
+ struct scad_geometry** geometries,
+ const size_t geometries_count,
+ struct scad_geometry** tools,
+ const size_t tools_count,
+ struct scad_geometry** out_geometry)
+{
+ res_T res = RES_OK;
+ int* tagout = NULL;
+ int** map = NULL;
+ size_t* mapn = NULL;
+ size_t tagoutn, mapnn = 0, sz1, sz2;
+ int* data1 = NULL;
+ int* data2 = NULL;
+ int ierr = 0;
+ struct scad_geometry* geom = NULL;
+ struct scad_device* dev = get_device();
+ struct mem_allocator* allocator = NULL;
+ int log;
+ enum scad_log_refcounting option;
+
+ if(!geometries || !geometries_count || !tools || !tools_count || !out_geometry) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ ERR(check_device(FUNC_NAME));
+ allocator = dev->allocator;
+ option = dev->options.Misc.LogOpenCascadeTagsRefCounting;
+ log = option == Scad_log_all;
+
+ ERR(gather_tags(geometries, geometries_count, &data1, &sz1));
+ ERR(gather_tags(tools, tools_count, &data2, &sz2));
+
+ /* We don't remove gmsh objects here; they are only removed when their tags are
+ * no longuer used by any star-cad geometry */
+ gmshModelOccFragment(data1, sz1, data2, sz2, &tagout, &tagoutn, &map, &mapn,
+ &mapnn, -1, 0, 0, &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+
+ ERR(scad_geometry_create(name, &geom));
+ geom->gmsh_dimTags_n = tagoutn;
+ geom->gmsh_dimTags = MEM_ALLOC(allocator, tagoutn * sizeof(*tagout));
+ if(!geom->gmsh_dimTags) {
+ res = RES_MEM_ERR;
+ goto error;
+ }
+ memcpy(geom->gmsh_dimTags, tagout, tagoutn * sizeof(*tagout));
+
+ ERR(device_register_tags(geom));
+
+exit:
+ if(out_geometry) *out_geometry = geom;
+ if(allocator) {
+ MEM_RM(allocator, data1);
+ MEM_RM(allocator, data2);
+ }
+ gmshFree(mapn);
+ gmshFree(tagout);
+ free_gmsh_map(map, mapnn);
+ return res;
+error:
+ if(geom) {
+ CHK(RES_OK == geometry_release(log, LOG_OUTPUT, geom));
+ geom = NULL;
+ }
+ if(tagout) gmshModelOccRemove(tagout, tagoutn, 1, &ierr);
+ goto exit;
+}
+
+
+
+res_T
+scad_geometry_boundary
+ (const char* name,
+ struct scad_geometry** geometries,
+ const size_t geometries_count,
+ struct scad_geometry** out_geometry)
+{
+ res_T res = RES_OK;
+ int* tagout = NULL;
+ size_t tagoutn, sz;
+ int* data = NULL;
+ int ierr = 0;
+ struct scad_geometry* geom = NULL;
+ struct scad_device* dev = get_device();
+ struct mem_allocator* allocator = NULL;
+ int log;
+ enum scad_log_refcounting option;
+
+ if(!geometries || !out_geometry) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ ERR(check_device(FUNC_NAME));
+ allocator = dev->allocator;
+ option = dev->options.Misc.LogOpenCascadeTagsRefCounting;
+ log = option == Scad_log_all;
+
+ ERR(gather_tags(geometries, geometries_count, &data, &sz));
+ gmshModelGetBoundary(data, sz, &tagout, &tagoutn, 1, 0, 0, &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+
+ ERR(scad_geometry_create(name, &geom));
+ geom->gmsh_dimTags_n = tagoutn;
+ geom->gmsh_dimTags = MEM_ALLOC(allocator, tagoutn * 2 * sizeof(*tagout));
+ if(!geom->gmsh_dimTags) {
+ res = RES_MEM_ERR;
+ goto error;
+ }
+ memcpy(geom->gmsh_dimTags, tagout, tagoutn * sizeof(*tagout));
+
+ ERR(device_register_tags(geom));
+
+exit:
+ if(allocator) MEM_RM(allocator, data);
+ if(out_geometry) *out_geometry = geom;
+ gmshFree(tagout);
+ return res;
+error:
+ if(geom) {
+ CHK(RES_OK == geometry_release(log, LOG_OUTPUT, geom));
+ geom = NULL;
+ }
+ if(tagout) gmshModelOccRemove(tagout, tagoutn, 1, &ierr);
+ goto exit;
+}
+
+res_T
+scad_step_import
+ (const char* filename,
+ const char* name,
+ struct scad_geometry*** out_geometry,
+ size_t* out_geometry_n)
+{
+ int ierr;
+ int* tagout = NULL;
+ size_t tagoutn, i, ga_sz;
+ struct str strname;
+ int name_initialized = 0;
+ struct scad_geometry** geom_array = NULL;
+ struct mem_allocator* allocator = NULL;
+ struct scad_device* dev = get_device();
+ res_T res = RES_OK;
+
+ if(!filename || !out_geometry || !out_geometry_n) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ ERR(check_device(FUNC_NAME));
+ allocator = dev->allocator;
+
+ gmshModelOccImportShapes(filename, &tagout, &tagoutn, 1, "step", &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+
+ ASSERT(tagoutn % 2 == 0);
+ ga_sz = tagoutn / 2;
+ allocator = get_device()->allocator;
+ geom_array = MEM_CALLOC(allocator, ga_sz, sizeof(*geom_array));
+ if(!geom_array) {
+ res = RES_MEM_ERR;
+ goto error;
+ }
+
+ str_init(allocator, &strname);
+ name_initialized = 1;
+ for(i=0; i<ga_sz; ++i) {
+ if (name) {
+ ERR(str_set(&strname, name));
+ ERR(str_append_printf(&strname,"_%lu", (unsigned long)i));
+ ERR(scad_geometry_create(str_cget(&strname), geom_array+i));
+ } else {
+ ERR(scad_geometry_create(NULL, geom_array+i));
+ }
+
+ geom_array[i]->gmsh_dimTags_n = 2;
+ geom_array[i]->gmsh_dimTags
+ = MEM_ALLOC(allocator, 2 * sizeof(*geom_array[i]->gmsh_dimTags));
+ if(!geom_array[i]->gmsh_dimTags) {
+ res = RES_MEM_ERR;
+ goto error;
+ }
+ geom_array[i]->gmsh_dimTags[0] = tagout[2*i];
+ geom_array[i]->gmsh_dimTags[1] = tagout[2*i+1];
+
+ ERR(device_register_tags(geom_array[i]));
+ }
+
+exit:
+ gmshFree(tagout);
+ if(out_geometry_n) *out_geometry_n = ga_sz ;
+ if(out_geometry) *out_geometry = geom_array;
+ if(name_initialized) str_release(&strname);
+ return res;
+error:
+ if(tagout) gmshModelOccRemove(tagout, tagoutn, 1, &ierr);
+ ga_sz = 0;
+ if(geom_array) {
+ for(i=0; i<ga_sz; ++i) {
+ if(geom_array[i]) SCAD(geometry_delete(geom_array[i]));
+ }
+ MEM_RM(allocator, geom_array);
+ geom_array = NULL;
+ }
+ goto exit;
+}
+
+
+res_T
+scad_geometry_normal
+ (struct scad_geometry* geom,
+ double p[3],
+ double N[3],
+ const char* name, /* Can be NULL */
+ struct scad_geometry** out_geometry)
+{
+ res_T res = RES_OK;
+ int ierr = 0;
+ size_t i;
+ int* data = NULL;
+ size_t sz = 0;
+ struct scad_geometry* surface = NULL;
+ struct scad_geometry* out = NULL;
+ struct scad_device* dev = get_device();
+ struct mem_allocator* allocator = NULL;
+
+ if(!geom || !p || !N || !out_geometry) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ ERR(check_device(FUNC_NAME));
+ allocator = dev->allocator;
+
+ if(geom->gmsh_dimTags[0] == 2) {
+ ERR(scad_geometry_copy(geom, NULL, &surface));
+ ERR(scad_synchronize());
+ } else if(geom->gmsh_dimTags[0] == 3) {
+ ERR(scad_geometry_boundary(NULL, &geom, 1, &surface));
+ } else {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ ERR(gather_tags(&surface, 1, &data, &sz));
+
+ for(i=0; sz/2; ++i) {
+ double* coord = NULL;
+ double* pcoord = NULL;
+ size_t pcoord_n;
+ size_t coord_n;
+ double* normals = NULL;
+ size_t normals_n;
+ int dim = data[2*i];
+ int tag = data[2*i + 1];
+
+ gmshModelGetParametrization(2, tag, p, 3, &pcoord, &pcoord_n, &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+
+ gmshModelGetValue(2, tag, pcoord, pcoord_n, &coord, &coord_n, &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+
+ if(d3_eq_eps(p, coord, 1e-6)) {
+ gmshModelGetNormal(tag, pcoord, pcoord_n, &normals, &normals_n, &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+
+ ERR(scad_geometry_create(name, &out));
+ out->gmsh_dimTags = MEM_ALLOC(allocator, 2 * sizeof(*out->gmsh_dimTags));
+ if(!out->gmsh_dimTags) {
+ res = RES_MEM_ERR;
+ goto error;
+ }
+ out->gmsh_dimTags_n = 2;
+ out->gmsh_dimTags[0] = dim;
+ out->gmsh_dimTags[1] = tag;
+ ERR(device_register_tags(out));
+
+ d3_set(N, normals);
+ gmshFree(coord);
+ gmshFree(pcoord);
+ gmshFree(normals);
+ break;
+ }
+ }
+
+exit:
+ if(out_geometry) *out_geometry = out;
+ if(allocator) MEM_RM(allocator, data);
+ if(surface) scad_geometry_delete(surface);
+ return res;
+error:
+ goto exit;
+}
+
+res_T
+scad_geometry_dilate
+ (struct scad_geometry* geom,
+ double center[3],
+ double scale[3])
+{
+ res_T res = RES_OK;
+ int ierr = 0;
+ int* data = NULL;
+ size_t sz = 0;
+ struct scad_device* dev = get_device();
+ struct mem_allocator* allocator = NULL;
+
+ if(!geom || !scale|| !center) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ ERR(check_device(FUNC_NAME));
+ allocator = dev->allocator;
+
+ ERR(gather_tags(&geom, 1, &data, &sz));
+
+ gmshModelOccDilate(data, sz, SPLIT3(center), SPLIT3(scale), &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+
+exit:
+ if(allocator) MEM_RM(allocator, data);
+ return res;
+error:
+ goto exit;
+}
diff --git a/src/scad_geometry.h b/src/scad_geometry.h
@@ -0,0 +1,37 @@
+/* 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/>. */
+
+#ifndef SCAD_PRIMITIVE_H
+#define SCAD_PRIMITIVE_H
+
+#include <stdlib.h>
+
+#include <rsys/rsys.h>
+#include <rsys/str.h>
+#include <rsys/logger.h>
+
+struct scad_geometry {
+ int* gmsh_dimTags;
+ size_t gmsh_dimTags_n;
+ struct str name;
+};
+
+LOCAL_SYM res_T
+geometry_release
+ (const int log,
+ const enum log_type log_type,
+ struct scad_geometry* geom);
+
+#endif
diff --git a/src/test.c b/src/test.c
@@ -1,48 +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/>. */
-
-#include "scad.h"
-
-#define ERR(Expr) if((res = (Expr)) != RES_OK) goto error; else (void)0
-
-int
-main(int argc, char* argv[])
-{
- res_T res = RES_OK;
- double p1[3] = {0, 0, 0};
- double p2[3] = {0.25, 0.25, 0.8};
- double d1[3] = {1, 1, 1};
- double d2[3] = {0.5, 0.5, 0.5};
- scad_geom_T box1 = SCAD_GEOM_NULL;
- scad_geom_T box2 = SCAD_GEOM_NULL;
- scad_geom_T cut = SCAD_GEOM_NULL;
-
- (void)argc; (void)argv;
-
- ERR(scad_init());
- ERR(scad_addbox(p1, d1, &box1));
- ERR(scad_addbox(p2, d2, &box2));
-
- ERR(scad_conformal_mesh());
-
-exit:
- scad_geom_release(box1);
- scad_geom_release(box2);
- scad_geom_release(cut);
- scad_release();
- return res;
-error:
- goto exit;
-}
diff --git a/src/test_api.c b/src/test_api.c
@@ -0,0 +1,265 @@
+/* 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/>. */
+
+#include "scad.h"
+#include "scad_geometry.h"
+#include "test_common.h"
+
+#include <rsys/rsys.h>
+#include <rsys/str.h>
+#include <rsys/math.h>
+#include <rsys/mem_allocator.h>
+#include <rsys/logger.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+
+static void
+get_position
+ (const size_t ivert, double pos[2], void* data)
+{
+ double* coord = data;
+ ASSERT(pos && coord);
+ pos[0] = coord[2*ivert];
+ pos[1] = coord[1+2*ivert];
+
+}
+
+int
+main(int argc, char* argv[])
+{
+ res_T res = RES_OK;
+ double p1[3] = {0, 0, 0};
+ double p2[3] = {0.25, 0.25, 0.8};
+ double d1[3] = {1, 1, 1};
+ double coord[] = {0, 1.6, 0.5, 0.9, 0.8, 0.6};
+ struct scad_geometry* geom1 = NULL;
+ struct scad_geometry* geom2 = NULL;
+ struct scad_geometry* geom = NULL;
+ struct scad_geometry* poly = NULL;
+ struct scad_geometry** geom_array = NULL;
+ struct scad_geometry* geoms[2];
+ struct scad_geometry* out_geoms[2];
+ struct mem_allocator allocator;
+ struct logger logger;
+ const char* name;
+ size_t i, c;
+
+ (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());
+ BAD(scad_set_options(NULL));
+ BAD(scad_synchronize());
+ BAD(scad_scene_clear());
+ BAD(scad_add_rectangle(NULL, p1, d1, &geom));
+ BAD(scad_add_disk(NULL, p1, 1, &geom));
+ BAD(scad_add_polygon(NULL, get_position, coord, 0, 3, &geom));
+ BAD(scad_add_box(NULL, p1, d1, &geom));
+ BAD(scad_add_cylinder(NULL, p1, d1, 2, 1, &geom));
+ BAD(scad_add_sphere(NULL, p1, 1, &geom));
+ BAD(scad_step_import("test.step", NULL, &geom_array, &c));
+ BAD(scad_scene_mesh());
+ BAD(scad_run_ui());
+ 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_finalize());
+
+ BAD(scad_finalize());
+ BAD(scad_set_options(NULL));
+ BAD(scad_synchronize());
+ BAD(scad_scene_clear());
+ BAD(scad_add_rectangle(NULL, p1, d1, &geom));
+ BAD(scad_add_disk(NULL, p1, 1, &geom));
+ BAD(scad_add_polygon(NULL, get_position, coord, 0, 3, &geom));
+ BAD(scad_add_box(NULL, p1, d1, &geom));
+ BAD(scad_add_cylinder(NULL, p1, d1, 2, 1, &geom));
+ BAD(scad_add_sphere(NULL, p1, 1, &geom));
+ BAD(scad_step_import("test.step", NULL, &geom_array, &c));
+ BAD(scad_scene_mesh());
+ BAD(scad_run_ui());
+ BAD(scad_geometry_get_name(geom, &name));
+
+ BAD(scad_initialize(&logger, &allocator, 4));
+ BAD(scad_initialize(&logger, &allocator, -1));
+
+ OK(scad_initialize(&logger, &allocator, 3));
+ OK(scad_add_sphere("sphere 1", p1, .1, &geom1));
+ OK(scad_add_sphere(NULL, p2, .2, &geom2));
+ geoms[0] = geom1;
+ geoms[1] = geom2;
+
+ OK(scad_add_sphere(NULL, p1, .1, &geom));
+ BAD(scad_geometry_delete(NULL));
+ OK(scad_geometry_delete(geom));
+
+ BAD(scad_geometry_get_count(NULL, &c));
+ BAD(scad_geometry_get_count(geom1, NULL));
+ OK(scad_geometry_get_count(geom1, &c));
+
+ BAD(scad_add_rectangle(NULL, NULL, d1, &geom));
+ BAD(scad_add_rectangle(NULL, p1, NULL, &geom));
+ BAD(scad_add_rectangle(NULL, p1, d1, NULL));
+ BAD(scad_add_rectangle("sphere 1", p1, d1, &geom)); /* Name already used */
+ OK(scad_add_rectangle(NULL, p1, d1, &geom));
+ OK(scad_geometry_delete(geom));
+
+ BAD(scad_add_disk(NULL, NULL, 1, &geom));
+ BAD(scad_add_disk(NULL, p1, 0, &geom));
+ BAD(scad_add_disk(NULL, p1, 1, NULL));
+ OK(scad_add_disk(NULL, p1, 1, &geom));
+ OK(scad_geometry_delete(geom));
+
+ BAD(scad_add_polygon(NULL, NULL, coord, 0, 3, &poly));
+ BAD(scad_add_polygon(NULL, get_position, coord, 0, 2, &poly));
+ BAD(scad_add_polygon(NULL, get_position, coord, 0, 2, &poly));
+ BAD(scad_add_polygon(NULL, get_position, coord, 0, 3, NULL));
+ OK(scad_add_polygon(NULL, get_position, coord, 0, 3, &poly));
+
+ BAD(scad_add_box(NULL, NULL, d1, &geom));
+ BAD(scad_add_box(NULL, p1, NULL, &geom));
+ BAD(scad_add_box(NULL, p1, d1, NULL));
+ OK(scad_add_box(NULL, p1, d1, &geom));
+ OK(scad_geometry_delete(geom));
+
+ BAD(scad_add_cylinder(NULL, NULL, d1, 2, 1, &geom));
+ BAD(scad_add_cylinder(NULL, p1, NULL, 2, 1, &geom));
+ BAD(scad_add_cylinder(NULL, p1, d1, 0, 1, &geom));
+ BAD(scad_add_cylinder(NULL, p1, d1, 2, -1, &geom));
+ BAD(scad_add_cylinder(NULL, p1, d1, 2, 3*PI, &geom));
+ BAD(scad_add_cylinder(NULL, p1, d1, 2, 1, NULL));
+ OK(scad_add_cylinder(NULL, p1, d1, 2, 1, &geom));
+ OK(scad_geometry_delete(geom));
+
+ BAD(scad_add_sphere(NULL, NULL, 1, &geom));
+ BAD(scad_add_sphere(NULL, p1, 0, &geom));
+ BAD(scad_add_sphere(NULL, p1, 1, NULL));
+ OK(scad_add_sphere(NULL, p1, 1, &geom));
+ OK(scad_geometry_delete(geom));
+
+ /* BAD(scad_fuse_geometries(NULL, NULL, 0, geoms, 2, &geom)); */
+ BAD(scad_fuse_geometries(NULL, geoms, 2, NULL, 0, &geom));
+ BAD(scad_fuse_geometries(NULL, NULL, 1, &geom2, 1, &geom));
+ BAD(scad_fuse_geometries(NULL, &geom1, 1, NULL, 1, &geom));
+ BAD(scad_fuse_geometries(NULL, &geom1, 1, &geom2, 1, NULL));
+ OK(scad_fuse_geometries(NULL, &geom1, 1, &geom2, 1, &geom));
+ OK(scad_geometry_delete(geom));
+
+ /* BAD(scad_cut_geometries(NULL, NULL, 0, geoms, 2, &geom)); */
+ BAD(scad_cut_geometries(NULL, geoms, 2, NULL, 0, &geom));
+ BAD(scad_cut_geometries(NULL, NULL, 1, &geom2, 1, &geom));
+ BAD(scad_cut_geometries(NULL, &geom1, 1, NULL, 1, &geom));
+ BAD(scad_cut_geometries(NULL, &geom1, 1, &geom2, 1, NULL));
+ OK(scad_cut_geometries(NULL, &geom1, 1, &geom2, 1, &geom));
+ OK(scad_geometry_delete(geom));
+
+ /* BAD(scad_intersect_geometries(NULL, NULL, 0, geoms, 2, &geom)); */
+ BAD(scad_intersect_geometries(NULL, geoms, 2, NULL, 0, &geom));
+ BAD(scad_intersect_geometries(NULL, NULL, 1, &geom2, 1, &geom));
+ BAD(scad_intersect_geometries(NULL, &geom1, 1, NULL, 1, &geom));
+ BAD(scad_intersect_geometries(NULL, &geom1, 1, &geom2, 1, NULL));
+ OK(scad_intersect_geometries(NULL, &geom1, 1, &geom2, 1, &geom));
+ OK(scad_geometry_delete(geom));
+
+ /* BAD(scad_geometries_common_boundaries(NULL, NULL, 0, geoms, 2, &geom)); */
+ BAD(scad_geometries_common_boundaries(NULL, geoms, 2, NULL, 0, &geom));
+ BAD(scad_geometries_common_boundaries(NULL, NULL, 1, &geom2, 1, &geom));
+ BAD(scad_geometries_common_boundaries(NULL, &geom1, 1, NULL, 1, &geom));
+ BAD(scad_geometries_common_boundaries(NULL, &geom1, 1, &geom2, 1, NULL));
+ OK(scad_geometries_common_boundaries(NULL, &geom1, 1, &geom2, 1, &geom));
+ OK(scad_geometry_delete(geom));
+
+ BAD(scad_geometries_partition(NULL, 2, 1, out_geoms));
+ BAD(scad_geometries_partition(geoms, 0, 1, out_geoms));
+ BAD(scad_geometries_partition(geoms, 2, 1, NULL));
+ OK(scad_geometries_partition(geoms, 2, 1, out_geoms));
+ OK(scad_geometry_delete(out_geoms[0]));
+ OK(scad_geometry_delete(out_geoms[1]));
+
+ BAD(scad_geometry_boundary(NULL, NULL, 0, &geom));
+ BAD(scad_geometry_boundary(NULL, &geom1, 1, NULL));
+ OK(scad_geometry_boundary(NULL, &geom1, 1, &geom));
+ OK(scad_geometry_delete(geom));
+
+ BAD(scad_geometry_copy(NULL, NULL, &geom));
+ BAD(scad_geometry_copy(geom1, NULL, NULL));
+ BAD(scad_geometry_copy(geom1, "sphere 1", NULL)); /* Name already in use */
+ OK(scad_geometry_copy(geom1, "Sphere 1", &geom));
+ OK(scad_geometry_delete(geom));
+
+ BAD(scad_geometry_rename(NULL, NULL));
+ BAD(scad_geometry_rename(geom2, "sphere 1")); /* Name already in use */
+ OK(scad_geometry_rename(geom2, NULL));
+
+ BAD(scad_geometry_translate(NULL, d1));
+ BAD(scad_geometry_translate(geom1, NULL));
+ OK(scad_geometry_translate(geom1, d1));
+
+ BAD(scad_geometry_rotate(NULL, p1, d1, 1));
+ BAD(scad_geometry_rotate(geom1, NULL, d1, 1));
+ BAD(scad_geometry_rotate(geom1, p1, NULL, 1));
+ OK(scad_geometry_rotate(geom1, p1, d1, 1));
+
+ BAD(scad_geometry_extrude(NULL, NULL, d1, &geom));
+ BAD(scad_geometry_extrude(poly, NULL, NULL, &geom));
+ BAD(scad_geometry_extrude(poly, NULL, d1, NULL));
+ OK(scad_geometry_extrude(poly, NULL, d1, &geom));
+ OK(scad_geometry_delete(poly));
+ OK(scad_geometry_delete(geom));
+
+ BAD(scad_scene_write(NULL));
+ ERR(scad_scene_write(""));
+ OK(scad_scene_write("/tmp/test.step"));
+
+ BAD(scad_step_import(NULL, "step", &geom_array, &c));
+ BAD(scad_step_import("/tmp/test.step", NULL, &geom_array, &c));
+ BAD(scad_step_import("/tmp/test.step", "step", NULL, &c));
+ BAD(scad_step_import("/tmp/test.step", "step", &geom_array, NULL));
+ ERR(scad_step_import("dont_exist.step", "step", &geom_array, &c));
+ OK(scad_step_import("/tmp/test.step", "step", &geom_array, &c));
+ for(i = 0; i < c; i++) {
+ OK(scad_geometry_delete(geom_array[i]));
+ }
+ MEM_RM(&allocator, geom_array);
+
+ BAD(scad_stl_export(NULL, NULL, 0, 0));
+ BAD(scad_stl_export(geom2, NULL, 0, 0)); /* geom2 has no name */
+ OK(scad_stl_export(geom2, "/tmp/test", 0, 0));
+ OK(scad_stl_export(geom1, NULL, 0, 0));
+
+ BAD(scad_stl_export_split(NULL, NULL, 0));
+ BAD(scad_stl_export_split(geom2, NULL, 0)); /* geom2 has no name */
+ OK(scad_stl_export_split(geom2, "/tmp/test", 0));
+ OK(scad_stl_export_split(geom1, NULL, 0));
+
+ BAD(scad_geometry_get_name(NULL, &name));
+ BAD(scad_geometry_get_name(geom1, NULL));
+ OK(scad_geometry_get_name(geom1, &name));
+ OK(scad_geometry_get_name(geom2, &name));
+
+ logger_release(&logger);
+ OK(scad_finalize());
+
+ check_memory_allocator(&allocator);
+ mem_shutdown_proxy_allocator(&allocator);
+ CHK(mem_allocated_size() == 0);
+
+ return (res == RES_OK) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/src/test_common.h b/src/test_common.h
@@ -0,0 +1,38 @@
+/* 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/>. */
+
+#include "scad.h"
+#include "scad_geometry.h"
+
+#include <rsys/rsys.h>
+#include <rsys/math.h>
+#include <rsys/mem_allocator.h>
+
+#include <stdlib.h>
+
+#define OK(Expr) CHK(RES_OK == (Expr))
+#define BAD(Expr) CHK(RES_BAD_ARG == (Expr))
+#define ERR(Expr) CHK(RES_OK != (Expr))
+
+static void
+check_memory_allocator(struct mem_allocator* allocator) {
+ if(MEM_ALLOCATED_SIZE(allocator)) {
+ char dump[4096];
+ MEM_DUMP(allocator, dump, sizeof(dump)/sizeof(char));
+ fprintf(stderr, "%s\n", dump);
+ FATAL("Memory leaks.\n");
+ }
+}
+
diff --git a/src/test_export.c b/src/test_export.c
@@ -0,0 +1,73 @@
+/* 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/>. */
+
+#include "scad.h"
+#include "scad_geometry.h"
+#include "test_common.h"
+
+#include <rsys/rsys.h>
+#include <rsys/math.h>
+#include <rsys/mem_allocator.h>
+#include <rsys/logger.h>
+
+#include <stdlib.h>
+
+int
+main(int argc, char* argv[])
+{
+ res_T res = RES_OK;
+ double p1[3] = {0, 0, 0};
+ double p2[3] = {4.25, 4.25, 4.8};
+ double d1[3] = {1, 1, 1};
+ struct scad_geometry* rectangle = NULL;
+ struct scad_geometry* cube = NULL;
+ 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));
+ logger_release(&logger);
+ OK(scad_initialize(&logger, &allocator, 3));
+
+ OK(scad_add_rectangle("rectangle", p1, d1, &rectangle));
+ OK(scad_add_box("cube", p1, d1, &cube));
+ OK(scad_add_box(NULL, p2, d1, geoms+1));
+
+ geoms[0] = cube;
+ OK(scad_fuse_geometries("cube2", geoms, 1, geoms+1, 1, &cube2));
+
+ OK(scad_scene_mesh());
+
+ OK(scad_stl_export(rectangle, NULL, 0, 0));
+ OK(scad_stl_export(rectangle, "bin_rectangle", 0, 1));
+
+ OK(scad_stl_export(cube, NULL, 0, 0));
+ OK(scad_stl_export(cube, "bin_cube", 0, 1));
+
+ OK(scad_stl_export(cube2, NULL, 0, 0));
+ OK(scad_stl_export(cube2, "bin_cube2", 0, 1));
+
+ OK(scad_finalize());
+
+ check_memory_allocator(&allocator);
+ mem_shutdown_proxy_allocator(&allocator);
+ CHK(mem_allocated_size() == 0);
+
+ return (res == RES_OK) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/src/test_partition.c b/src/test_partition.c
@@ -0,0 +1,112 @@
+/* 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/>. */
+
+#define _POSIX_C_SOURCE 200112L
+
+#include "scad.h"
+#include "scad_geometry.h"
+#include "test_common.h"
+
+#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>
+
+static res_T
+two_geoms
+ (double x,
+ const int allow_overlapping,
+ struct mem_allocator* allocator,
+ struct logger* logger)
+{
+ 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));
+
+ /* 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("cube2", p2, d2, geoms+1));
+
+ res = scad_geometries_partition(geoms, 2, allow_overlapping, out_geoms);
+ if(res != RES_OK) goto end;
+
+ OK(scad_scene_mesh());
+
+ if(allow_overlapping) {
+ snprintf(name, sizeof(name), "part_%g_1o", x);
+ OK(scad_stl_export(out_geoms[0], name, 0, 0));
+ snprintf(name, sizeof(name), "part_%g_2o", x);
+ OK(scad_stl_export(out_geoms[1], name, 0, 1));
+ } else {
+ snprintf(name, sizeof(name), "part_%g_1", x);
+ OK(scad_stl_export(geoms[0], name, 0, 0));
+ snprintf(name, sizeof(name), "part_%g_2", x);
+ OK(scad_stl_export(geoms[1], name, 0, 1));
+ }
+
+end:
+ OK(scad_finalize());
+ return res;
+}
+
+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));
+
+ /* First with overlapping geometries */
+ BAD(two_geoms(0.9, 0, &allocator, &logger));
+ OK(two_geoms(0.9, 1, &allocator, &logger));
+
+ logger_release(&logger);
+
+ check_memory_allocator(&allocator);
+ mem_shutdown_proxy_allocator(&allocator);
+ CHK(mem_allocated_size() == 0);
+
+ return (res == RES_OK) ? EXIT_SUCCESS : EXIT_FAILURE;
+}