commit 9a934b6abffb7b5f7190174ce7d0f42ffc2b8cc8
parent 55aff2e562fb349960a9408928d8208f7613c51e
Author: Christophe Coustet <christophe.coustet@meso-star.com>
Date: Fri, 15 Jul 2022 17:24:38 +0200
Split in more files; make scad types opaque
Diffstat:
| M | cmake/CMakeLists.txt | | | 11 | ++++++++--- |
| M | src/scad.c | | | 810 | ++++++++++++++++++++++++++++++------------------------------------------------- |
| M | src/scad.h | | | 252 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------- |
| A | src/scad_c.h | | | 36 | ++++++++++++++++++++++++++++++++++++ |
| A | src/scad_device.c | | | 197 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | src/scad_device.h | | | 82 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | src/scad_geometry.c | | | 764 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | src/scad_geometry.h | | | 39 | +++++++++++++++++++++++++++++++++++++++ |
| A | src/scad_scene.c | | | 172 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | src/scad_scene.h | | | 55 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| M | src/test1.c | | | 79 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------- |
11 files changed, 1912 insertions(+), 585 deletions(-)
diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt
@@ -42,10 +42,15 @@ set(VERSION_PATCH 0)
set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH})
set(SCAD_FILES_SRC
- scad.c)
+ scad.c
+ scad_scene.c
+ scad_device.c
+ scad_geometry.c)
set(SCAD_FILES_INC_API scad.h)
set(SCAD_FILES_INC
- )
+ scad_scene.h
+ 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'
@@ -55,7 +60,7 @@ 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_LIBRARIES})
+target_link_libraries(scad RSys ${GMSH_LIBRARIES} m)
set_target_properties(scad PROPERTIES
DEFINE_SYMBOL SCAD_SHARED_BUILD
diff --git a/src/scad.c b/src/scad.c
@@ -14,543 +14,336 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include "scad.h"
+#include "scad_c.h"
+#include "scad_device.h"
+#include "scad_scene.h"
+#include "scad_geometry.h"
#include <gmshc.h>
#include <rsys/cstr.h>
+#include <rsys/rsys.h>
#include <rsys/str.h>
+#include <rsys/float3.h>
#include <rsys/stretchy_array.h>
#include <stdio.h>
-/******************************************************************************
- * Helper functions
- *****************************************************************************/
-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;
-}
-
-/******************************************************************************
- * Exported functions
- *****************************************************************************/
-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);
- if((res = gmsh_err_to_res_T(ierr)) != RES_OK) goto error;
- gmshOptionSetNumber("Mesh.MeshSizeFromPoints", 0, &ierr);
- if((res = gmsh_err_to_res_T(ierr)) != RES_OK) goto error;
- gmshOptionSetNumber("Mesh.MeshSizeFromCurvature", 1, &ierr);
- if((res = gmsh_err_to_res_T(ierr)) != RES_OK) goto error;
- gmshOptionSetNumber("Mesh.MinimumElementsPerTwoPi", 36, &ierr);
- if((res = gmsh_err_to_res_T(ierr)) != RES_OK) goto error;
- gmshOptionSetNumber("Mesh.MeshSizeExtendFromBoundary", 0, &ierr);
- if((res = gmsh_err_to_res_T(ierr)) != RES_OK) goto error;
-
-exit:
- return res;
-error:
- fprintf(stderr, "%s: can't initialize gmsh -- %s\n",
- FUNC_NAME, res_to_cstr(res));
- goto exit;
-}
-
-res_T
-scad_release(void)
-{
- res_T res = RES_OK;
- int ierr = 0;
-
- gmshFinalize(&ierr);
- if((res = gmsh_err_to_res_T(ierr)) != RES_OK) goto error;
-
-exit:
- return res;
-error:
- fprintf(stderr, "%s: can't release gmsh -- %s\n", FUNC_NAME, res_to_cstr(res));
- goto exit;
-}
-
-res_T
-scad_synchronize(void)
-{
- int ierr;
- gmshModelOccSynchronize(&ierr);
- return gmsh_err_to_res_T(ierr);
-}
+#define OKP(Expr) if((Expr) < 0) { res=RES_IO_ERR; goto error; }
-res_T
-scad_run_ui(void)
-{
- int ierr;
- gmshFltkRun(&ierr);
- return gmsh_err_to_res_T(ierr);
-}
-
-res_T
-scad_geom_release(scad_geom_T geom)
-{
- sa_release(geom);
- return RES_OK;
-}
-
-res_T
-scad_concat(scad_geom_T* geom1, const scad_geom_T geom2)
+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;
size_t i;
+ FILE* stl_file = NULL;
+ unsigned cpt;
- if(!geom1 || !geom2) {
- fprintf(stderr, "%s: can't concat\n", FUNC_NAME);
- res = RES_BAD_ARG;
- goto error;
- }
-
- FOR_EACH(i, 0, sa_size(geom2)) {
- sa_push(*geom1, geom2[i]);
- }
-
-exit:
- return res;
-error:
- goto exit;
-}
-
-res_T
-scad_addbox
- (const double xyz[3],
- const double dxdydz[3],
- scad_geom_T* geom)
-{
- int ierr = 0;
- int tag = 0;
- res_T res = RES_OK;
-
- if(!xyz || !dxdydz) {
- fprintf(stderr, "%s: invalid data!\n", FUNC_NAME);
- res = RES_BAD_ARG;
- goto error;
- }
-
- tag = gmshModelOccAddBox(SPLIT3(xyz), SPLIT3(dxdydz), -1, &ierr);
- if((res = gmsh_err_to_res_T(ierr)) != RES_OK) {
- fprintf(stderr, "%s: can't create box -- %s\n", FUNC_NAME, res_to_cstr(res));
- goto error;
- }
-
- if(*geom != NULL) *geom = NULL;
- sa_push(*geom,3);
- sa_push(*geom,tag);
-
-exit:
- return res;
-error:
- goto exit;
-}
-
-res_T
-scad_addcylinder
- (const double xyz[3],
- const double axis[3],
- const double rad,
- const double angle,
- scad_geom_T* geom)
-{
- int ierr = 0;
- int tag = 0;
- res_T res = RES_OK;
-
- if(!xyz || !axis) {
- fprintf(stderr, "%s: invalid data!\n", FUNC_NAME);
- res = RES_BAD_ARG;
- goto error;
- }
-
- tag = gmshModelOccAddCylinder(SPLIT3(xyz), SPLIT3(axis), rad, -1, angle, &ierr);
- if((res = gmsh_err_to_res_T(ierr)) != RES_OK) {
- fprintf(stderr, "%s: can't create cylinder -- %s\n",
- FUNC_NAME, res_to_cstr(res));
- goto error;
- }
-
- if(*geom != NULL) *geom = NULL;
- sa_push(*geom,3);
- sa_push(*geom,tag);
-
-exit:
- return res;
-error:
- goto exit;
-}
-
-int
-scad_addsphere
- (const double xyz[3],
- const double rad,
- scad_geom_T* geom)
-{
- int ierr = 0;
- int tag = 0;
- res_T res = RES_OK;
-
- if(!xyz) {
- fprintf(stderr,"%s: invalid data!\n", FUNC_NAME);
- res = RES_BAD_ARG;
- goto error;
- }
-
- tag = gmshModelOccAddSphere(SPLIT3(xyz), rad, -1, -PI/2, PI/2, 2*PI, &ierr);
- if((res = gmsh_err_to_res_T(ierr)) != RES_OK) {
- fprintf(stderr, "%s: can't create sphere -- %s\n",
- FUNC_NAME, res_to_cstr(res));
- goto exit;
- }
-
- if(*geom != NULL) *geom = NULL;
- sa_push(*geom,3);
- sa_push(*geom,tag);
-
-exit:
- return res;
-error:
- goto exit;
-}
-
-res_T
-scad_remove(scad_geom_T geom)
-{
- int ierr = 0;
- res_T res = RES_OK;
-
- gmshModelOccRemove(geom, sa_size(geom), 0, &ierr);
- if((res = gmsh_err_to_res_T(ierr)) != RES_OK) {
- fprintf(stderr, "%s: can't remove geometry -- %s\n",
- FUNC_NAME, res_to_cstr(res));
- goto error;
- }
-
-exit:
- return res;
-error:
- goto exit;
-}
-
-res_T
-scad_fuse
- (const scad_geom_T geom1,
- const scad_geom_T geom2,
- scad_geom_T* out,
- const int remove)
-{
- int* tagout = NULL;
- int** map = NULL;
- size_t* mapn = NULL;
- size_t tagoutn, mapnn, i;
- int ierr = 0;
- res_T res = RES_OK;
+ ASSERT(filename && coord && coord_n);
- gmshModelOccFuse(geom1, sa_size(geom1), geom2, sa_size(geom2), &tagout,
- &tagoutn, &map, &mapn, &mapnn, -1, remove, remove, &ierr);
- if((res = gmsh_err_to_res_T(ierr)) != RES_OK) {
- fprintf(stderr, "%s: fuse not possible -- %s\n", FUNC_NAME, res_to_cstr(res));
+ stl_file = fopen(filename, "w");
+ if(!stl_file) {
+ res = RES_IO_ERR;
goto error;
}
- FOR_EACH(i, 0, tagoutn) {
- sa_push(*out, tagout[i]);
+ 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;
+ float n[3];
+ float vtx[3][3];
+ float tmp[3], edge1[3], edge2[3];;
+ f3_set_d3(vtx[0], coord[i]+j+0);
+ f3_set_d3(vtx[1], coord[i]+j+3);
+ f3_set_d3(vtx[2], coord[i]+j+6);
+ f3_sub(edge1, vtx[1], vtx[0]);
+ f3_sub(edge2, vtx[2], vtx[0]);
+ f3_cross(tmp, edge1, edge2);
+ f3_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 %g %g %g\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(tagout) free(tagout);
- if(mapn) free(mapn);
- if(map) free(map);
+ if(stl_file) fclose(stl_file);
return res;
error:
+ fprintf(stderr, "Error: could not export to STL file '%s': %s\n",
+ filename, res_to_cstr(res));
goto exit;
}
-
-
-res_T
-scad_cut
- (const scad_geom_T geom1,
- const scad_geom_T geom2,
- scad_geom_T* out,
- const int remove)
+
+static res_T
+write_binary_stl
+ (const char* filename,
+ const unsigned trg_count,
+ double* const* coord,
+ const size_t* coord_n)
{
- int* tagout = NULL;
- int** map = NULL;
- size_t* mapn = NULL;
- size_t tagoutn, mapnn, i;
- int ierr = 0;
res_T res = RES_OK;
+ size_t i;
+ char header[80] = "Binary STL";
+ FILE* stl_file = NULL;
+ unsigned cpt;
- gmshModelOccCut(geom1, sa_size(geom1), geom2, sa_size(geom2), &tagout,
- &tagoutn, &map, &mapn, &mapnn, -1, remove, remove, &ierr);
- if((res = gmsh_err_to_res_T(ierr)) != RES_OK) {
- fprintf(stderr, "%s: cut not possible -- %s\n", FUNC_NAME, res_to_cstr(res));
- goto error;
- }
-
- if(*out) sa_clear(*out);
- FOR_EACH(i, 0, tagoutn) {
- sa_push(*out, tagout[i]);
- }
-
-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)
-{
- int* tagout = NULL;
- int** map = NULL;
- size_t* mapn = NULL;
- size_t tagoutn, mapnn, i;
- int ierr = 0;
- res_T res = RES_OK;
+ ASSERT(filename && coord && coord_n);
- gmshModelOccIntersect(geom1, sa_size(geom1), geom2, sa_size(geom2), &tagout,
- &tagoutn, &map, &mapn, &mapnn, -1, remove, remove, &ierr);
- if((res = gmsh_err_to_res_T(ierr)) != RES_OK) {
- fprintf(stderr, "%s: intersection not possible -- %s\n",
- FUNC_NAME, res_to_cstr(res));
+ stl_file = fopen(filename, "wb");
+ if(!stl_file) {
+ res = RES_IO_ERR;
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);
- gmshModelGetBoundary(geom2, sa_size(geom2), &bound2, &n2, 1, 0, 0, &ierr);
- gmshModelOccIntersect(bound1, n1, bound2, n2, &tagout, &tagoutn, &map,
- &mapn, &mapnn, -1, 0/*no delete*/, 0/*no delete*/, &ierr);
- }
-
- FOR_EACH(i, 0, tagoutn) {
- sa_push(*out, tagout[i]);
- }
-
-exit:
- if(tagout) free(tagout);
- if(mapn) free(mapn);
- if(map) free(map);
- return res;
-error:
- goto exit;
-}
-
-
-res_T
-scad_fragment
- (const scad_geom_T geom1,
- const scad_geom_T geom2,
- scad_geom_T* out,
- const int remove)
-{
- int* tagout = NULL;
- int** map = NULL;
- size_t* mapn = NULL;
- size_t tagoutn, mapnn, i;
- int ierr = 0;
- res_T res = RES_OK;
-
- gmshModelOccFragment(geom1, sa_size(geom1), geom2, sa_size(geom2), &tagout,
- &tagoutn, &map, &mapn, &mapnn, -1, remove, remove, &ierr);
- if((res = gmsh_err_to_res_T(ierr)) != RES_OK) {
- fprintf(stderr, "%s: fragment not possible -- %s\n",
- FUNC_NAME, res_to_cstr(res));
+ if(1 != fwrite(header, sizeof(header), 1, stl_file)) {
+ res = RES_IO_ERR;
goto error;
}
- FOR_EACH(i, 0, tagoutn) {
- sa_push(*out, tagout[i]);
- }
-
-exit:
- if(tagout) free(tagout);
- if(mapn) free(mapn);
- if(map) free(map);
- return res;
-error:
- goto exit;
-}
-
-res_T
-scad_boundary(const scad_geom_T geom1, scad_geom_T* out)
-{
- int* b = NULL;
- size_t i, n;
- int ierr = 0;
- res_T res = RES_OK;
-
- gmshModelGetBoundary(geom1, sa_size(geom1), &b, &n, 1, 0, 0, &ierr);
- if((res = gmsh_err_to_res_T(ierr)) != RES_OK) {
- fprintf(stderr, "%s: get boundary not possible -- %s\n",
- FUNC_NAME, res_to_cstr(res));
+ if(1 != fwrite(&trg_count, 4, 1, stl_file)) {
+ res = RES_IO_ERR;
goto error;
}
- FOR_EACH(i, 0, n) {
- sa_push(*out, b[i]);
+ 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(b) free(b);
+ if(stl_file) fclose(stl_file);
return res;
error:
+ fprintf(stderr, "Error: could not export to STL file '%s': %s\n",
+ filename, res_to_cstr(res));
goto exit;
}
-
-res_T
-scad_translate
-(scad_geom_T geom, const double dxdydz[3])
+
+static res_T
+write_stl
+ (const char* filename,
+ const unsigned trg_count,
+ double* const* coord,
+ const size_t* coord_n,
+ const int binary)
{
- int ierr = 0;
- res_T res = RES_OK;
-
- if(!geom || !dxdydz){
- fprintf(stderr,"%s: invalid data!\n", FUNC_NAME);
- res = RES_BAD_ARG;
- goto error;
- }
-
- gmshModelOccTranslate(geom, sa_size(geom), SPLIT3(dxdydz), &ierr);
- if((res = gmsh_err_to_res_T(ierr)) != RES_OK) {
- fprintf(stderr, "%s: translation not possible -- %s\n",
- FUNC_NAME, res_to_cstr(res));
- goto error;
- }
-
-exit:
- return res;
-error:
- 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_rotate
- (scad_geom_T geom,
- const double pt[3],
- const double axis[3],
- const double angle)
+scad_device_synchronize
+ (struct scad_device* device)
{
- int ierr = 0;
- res_T res = RES_OK;
-
- if(!geom || !pt || !axis){
- fprintf(stderr, "%s: invalid data!\n", FUNC_NAME);
- res = RES_BAD_ARG;
- goto error;
- }
-
- gmshModelOccRotate(geom, sa_size(geom), SPLIT3(pt), SPLIT3(axis), angle, &ierr);
- if((res = gmsh_err_to_res_T(ierr)) != RES_OK) {
- fprintf(stderr, "%s: rotation not possible -- %s\n",
- FUNC_NAME, res_to_cstr(res));
- goto error;
- }
-
-exit:
- return res;
-error:
- goto exit;
+ int ierr;
+ gmshModelOccSynchronize(&ierr);
+ device->need_synchro = 0;
+ return gmsh_err_to_res_T(ierr);
}
res_T
-scad_conformal_mesh(void)
+scad_run_ui(void)
{
- int* dimTags = NULL;
- size_t dimTags_n;
- int ierr = 0;
-
- gmshModelOccSynchronize(&ierr);
- gmshModelOccGetEntities(&dimTags, &dimTags_n, 3, &ierr);
- if( ierr == 0 && dimTags_n > 2) gmshModelOccRemoveAllDuplicates(&ierr);
- gmshModelOccSynchronize(&ierr);
- gmshModelMeshGenerate(2, &ierr);
+ int ierr;
+ gmshFltkRun(&ierr);
return gmsh_err_to_res_T(ierr);
}
res_T
-scad_stl_export(const scad_geom_T geom, char *prefix)
+scad_stl_export
+ (struct scad_geometry* geometry,
+ const char* prefix,
+ const int binary)
{
struct str filename;
int* tagout = NULL;
int* tags = NULL;
+ 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;
+ int* dimTags = 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;
- str_init(NULL, &filename);
- str_set(&filename, prefix);
- str_append(&filename, ".stl");
+ if(!geometry) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
- if(geom[0] == 2) {
- tagoutn = sa_size(geom);
- FOR_EACH(i, 0, tagoutn/2){
- sa_push(tags, geom[2*i + 1]);
- }
- group = gmshModelAddPhysicalGroup(2, tags, tagoutn/2, -1, &ierr);
- if((res = gmsh_err_to_res_T(ierr)) != RES_OK) goto error;
+ if(geometry->scene->device->need_synchro) {
+ ERR(scad_device_synchronize(geometry->scene->device));
+ }
+
+ sz = geometry->gmsh_dimTags_n;
+ data = geometry->gmsh_dimTags;
+ ASSERT(sz > 0 && sz % 2 == 0);
+ str_init(geometry->scene->device->allocator, &filename);
+ str_initialized = 1;
+ if(prefix) {
+ ERR(str_set(&filename, prefix));
} else {
- FOR_EACH(i, 0, sa_size(geom)/2) {
- gmshModelMeshSetOutwardOrientation(geom[2*i+1], &ierr);
+ if(str_len(&geometry->name) == 0) {
+ res = RES_BAD_ARG;
+ goto error;
}
-
- gmshModelGetBoundary(geom, sa_size(geom),&tagout, &tagoutn, 1, 0, 0, &ierr);
- if((res = gmsh_err_to_res_T(ierr)) != RES_OK) goto error;
-
- FOR_EACH(i, 0, tagoutn/2) {
- sa_push(tags, tagout[2*i + 1]);
+ ERR(str_copy(&filename, &geometry->name));
+ }
+ ERR(str_append(&filename, ".stl"));
+
+ if(data[0] == 3) { /* geometry is 3D */
+ size_t dimTags_n;
+ size_t tcount;
+
+ ERR(scad_device_synchronize(geometry->scene->device));
+ gmshModelOccGetEntities(&dimTags, &dimTags_n, 3, &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+ if(dimTags_n > 100000) {
+ size_t dt_n;
+ gmshModelOccRemoveAllDuplicates(&ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+ gmshModelOccGetEntities(&dimTags, &dt_n, 3, &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+#if 0
+ if(dimTags_n != dt_n) {
+ /* Entities count changed when removing duplicates !
+ * Some entities are overlapping: error. */
+ fprintf(stderr, "Invalid model, overlappig entities detected.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ /* TODO: implement early detection in add_xxx functions and use logger.
+ * Use a non-destructive command (unlike remove duplicate).
+ * (could be an option, e.g. reject_overlapping_entities, default 0) */
+ }
+#endif
+ }
+ ERR(scad_device_synchronize(geometry->scene->device));
+ gmshModelMeshGenerate(2, &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+ ERR(scad_device_synchronize(geometry->scene->device));
+
+ gmshModelGetBoundary(data, sz, &tagout, &tagoutn, 1, 0, 0, &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+ ASSERT(tagoutn % 2 == 0);
+
+ /* Allocate room for arrays
+ * TODO: use allocator */
+ nodeTags = calloc(tagoutn/2, sizeof(*nodeTags));
+ nodeTags_n = calloc(tagoutn/2, sizeof(*nodeTags_n));
+ coord = calloc(tagoutn/2, sizeof(*coord));
+ coord_n = calloc(tagoutn/2, sizeof(*coord_n));
+ pCoord = calloc(tagoutn/2, sizeof(*pCoord));
+ pCoord_n = calloc(tagoutn/2, sizeof(*pCoord_n));
+ if(!nodeTags || !nodeTags_n || !coord || !coord_n || !pCoord || !pCoord_n) {
+ res = RES_MEM_ERR;
+ goto error;
}
- group = gmshModelAddPhysicalGroup(2, tags, tagoutn/2, -1, &ierr);
- if((res = gmsh_err_to_res_T(ierr)) != RES_OK) goto error;
- }
+ tcount = 0;
+ for(i = 0; i < tagoutn/2; i++) {
+ gmshModelMeshGetNodesByElementType(2, nodeTags+i, nodeTags_n+i,
+ coord+i, coord_n+i, pCoord+i, pCoord_n+i, tagout[2*i+1], 0, &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+ ASSERT(coord_n[i] % 9 == 0);
+ ASSERT(nodeTags_n[i] % 3 == 0);
+ tcount += coord_n[i];
+ }
+ tcount /= 9;
+ ASSERT(tcount <= UINT_MAX);
+
+ ERR(write_stl(str_cget(&filename), (unsigned)tcount, coord, coord_n, binary));
+ } else { /* Geometry is 2D */
+ FOR_EACH(i, 0, sz/2){
+ ASSERT(data[2*i] == 2);
+ sa_push(tags, data[2*i + 1]);
+ }
+ group = gmshModelAddPhysicalGroup(2, tags, sz/2, -1, &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
- gmshWrite(str_get(&filename), &ierr);
- if((res = gmsh_err_to_res_T(ierr)) != RES_OK) goto error;
+ gmshWrite(str_cget(&filename), &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
- dimtag[0]=2;
- dimtag[1]=group;
- gmshModelRemovePhysicalGroups(dimtag, 2, &ierr);
- if((res = gmsh_err_to_res_T(ierr)) != RES_OK) goto error;
+ dimtag[0]=2;
+ dimtag[1]=group;
+ gmshModelRemovePhysicalGroups(dimtag, 2, &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+ }
exit:
- str_release(&filename);
+ if(str_initialized) str_release(&filename);
if(tagout) free(tagout);
if(tags) sa_release(tags);
+ if(nodeTags) {
+ for(i = 0; i < tagoutn/2; i++) free(nodeTags[i]);
+ free(nodeTags);
+ }
+ if(nodeTags_n) free(nodeTags_n);
+ if(coord) {
+ for(i = 0; i < tagoutn/2; i++) free(coord[i]);
+ free(coord);
+ }
+ if(coord_n) free(coord_n);
+ if(pCoord) {
+ for(i = 0; i < tagoutn/2; i++) free(pCoord[i]);
+ free(pCoord);
+ }
+ if(pCoord_n) free(pCoord_n);
+ if(dimTags) free(dimTags);
return res;
error:
fprintf(stderr, "%s: could not export to STL -- %s\n",
@@ -559,79 +352,98 @@ error:
}
res_T
-scad_stl_export_split(const scad_geom_T geom, char *prefix)
+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;
- int* tags = 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;
- str_init(NULL, &filename);
+ (void)binary;
- if(geom[0] == 2) {
- tagoutn = sa_size(geom);
- FOR_EACH(i, 0, tagoutn/2) {
- char num[32];
+ if(!geometry) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
- group = gmshModelAddPhysicalGroup(2, &geom[2*i+1], 1, -1, &ierr);
- if((res = gmsh_err_to_res_T(ierr)) != RES_OK) goto error;
+ if(geometry->scene->device->need_synchro) {
+ ERR(scad_device_synchronize(geometry->scene->device));
+ }
- /* TODO move in a function
- * TODO check the str return code */
- str_set(&filename, prefix);
- str_append_char(&filename, '_');
- sprintf(num, "%lu", (unsigned long)i); /* FIXME handle possible overflow */
- str_append(&filename, num);
- str_append(&filename, ".stl");
- gmshWrite(str_get(&filename), &ierr);
- if((res = gmsh_err_to_res_T(ierr)) != RES_OK) goto error;
+ sz = geometry->gmsh_dimTags_n;
+ data = geometry->gmsh_dimTags;
+ ASSERT(sz % 2 == 0);
+
+ str_init(geometry->scene->device->allocator, &filename_root);
+ str_init(geometry->scene->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);
+ 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));
dimtag[0]=2;
dimtag[1]=group;
gmshModelRemovePhysicalGroups(dimtag, 2, &ierr);
- if((res = gmsh_err_to_res_T(ierr)) != RES_OK) goto error;
+ ERR(gmsh_err_to_res_T(ierr));
}
-
} else {
-
- FOR_EACH(i, 0, sa_size(geom)/2) {
- gmshModelMeshSetOutwardOrientation(geom[2*i+1], &ierr);
+ FOR_EACH(i, 0, sz/2) {
+ ASSERT(data[2*i] == 3);
+ gmshModelMeshSetOutwardOrientation(data[2*i+1], &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
}
- gmshModelGetBoundary(geom, sa_size(geom), &tagout, &tagoutn, 1, 0, 0, &ierr);
- if((res = gmsh_err_to_res_T(ierr)) != RES_OK) goto error;
+ gmshModelGetBoundary(data, sz, &tagout, &tagoutn, 1, 0, 0, &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
FOR_EACH(i, 0, tagoutn/2){
- char num[32];
-
- group = gmshModelAddPhysicalGroup(2, &tagout[2*i+1], 1, -1, &ierr);
- if((res = gmsh_err_to_res_T(ierr)) != RES_OK) goto error;
-
- /* TODO move in a function
- * TODO check the str return code */
- str_set(&filename, prefix);
- str_append_char(&filename, '_');
- sprintf(num, "%lu", (unsigned long)i); /* FIXME handle possible overflow */
- str_append(&filename, num);
- str_append(&filename, ".stl");
- gmshWrite(str_get(&filename), &ierr);
- if((res = gmsh_err_to_res_T(ierr)) != RES_OK) goto error;
+ 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));
dimtag[0]=2;
dimtag[1]=group;
gmshModelRemovePhysicalGroups(dimtag, 2, &ierr);
- if((res = gmsh_err_to_res_T(ierr)) != RES_OK) goto error;
+ ERR(gmsh_err_to_res_T(ierr));
}
}
exit:
- str_release(&filename);
+ if(str_initialized) {
+ str_release(&filename_root);
+ str_release(&filename);
+ }
if(tagout) free(tagout);
- if(tags) sa_release(tags);
return res;
error:
goto exit;
diff --git a/src/scad.h b/src/scad.h
@@ -27,125 +27,245 @@
#define SCAD_API extern IMPORT_SYM
#endif
-/* 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__;
+/* 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;
+
+/* Forward declaration of scad opaque data types */
+struct scad_device; /* Entry point of the library */
+struct scad_scene; /* Collection of geometry items */
+struct scad_geometry; /* Wrapping of dimTags gmsh description */
+
+/* A type to specify options for devices */
+struct scad_device_param {
+ struct {
+ double StlOneSolidPerSurface;
+ double MeshSizeFromPoints;
+ double MeshSizeFromCurvature;
+ double MinimumElementsPerTwoPi;
+ double MeshSizeExtendFromBoundary;
+ } Mesh;
+};
+
+#define SCAD_INIT_DEFAULT__ \
+ { { 2, 0, 1, 36, 0 } }
+
+static const struct scad_device_param SCAD_INIT_DEFAULT = SCAD_INIT_DEFAULT__;
BEGIN_DECLS
-/******************************************************************************
- * API
- *****************************************************************************/
+/*******************************************************************************
+ * Device API - A device is the entry point of the scad library.
+ * Applications use a scad_device to create others scad resources.
+ ******************************************************************************/
SCAD_API res_T
-scad_init
- (void);
+scad_device_create
+ (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 */
+ struct scad_device** dev);
+
+res_T
+scad_device_set_params
+ (struct scad_device* device,
+ const struct scad_device_param* params);
+/* Explicitly synchronize current state with regard to recent geometry changes.
+ * Most synchronize calls should be automatically triggered when needed. */
SCAD_API res_T
-scad_release
- (void);
+scad_device_synchronize
+ (struct scad_device* device);
SCAD_API res_T
-scad_synchronize
- (void);
+scad_device_ref_get
+ (struct scad_device* dev);
-SCAD_API res_T /* FIXME remove this */
-scad_run_ui
- (void);
+SCAD_API res_T
+scad_device_ref_put
+ (struct scad_device* dev);
+
+/*******************************************************************************
+ * Scene API - A scene is a collection of geometries.
+ ******************************************************************************/
+SCAD_API res_T
+scad_scene_create
+ (struct scad_device* device,
+ struct scad_scene** out_scene);
+
+SCAD_API res_T
+scad_scene_ref_get
+ (struct scad_scene* scene);
-/* Remove the handler but not the geometry. Use scad_geom_remove to remove it.
- * TODO where is scad_geom_remove? */
SCAD_API res_T
-scad_geom_release
- (scad_geom_T geom);
+scad_scene_ref_put
+ (struct scad_scene* scene);
+
+/*******************************************************************************
+ * 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 by scene.
+ ******************************************************************************/
+/* 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_addbox
- (const double xyz[3],
+scad_scene_add_box
+ (struct scad_scene* scene,
+ const char* name, /* Can be NULL */
+ const double xyz[3],
const double dxdydz[3],
- scad_geom_T* geom);
+ struct scad_geometry** box); /* Can be NULL: no handler returned. */
+/* 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_addcylinder
- (const double xyz[3],
+scad_scene_add_cylinder
+ (struct scad_scene* scene,
+ const char* name, /* Can be NULL */
+ const double xyz[3],
const double axis[3],
const double rad,
const double angle,
- scad_geom_T* geom);
+ struct scad_geometry** cylinder); /* Can be NULL: no handler returned. */
+/* Add a sphere of center `xyz' and radius `rad' to the scene. */
SCAD_API res_T
-scad_addsphere
- (const double xyz[3],
+scad_scene_add_sphere
+ (struct scad_scene* scene,
+ const char* name, /* Can be NULL */
+ const double xyz[3],
const double rad,
- scad_geom_T* geom);
+ struct scad_geometry** sphere); /* Can be NULL: no handler returned. */
+/* Create a group of geometries of dimension `dim' from an array of geometries.
+ * All of the geometries must have been added to scene and must have dimension
+ * dim. */
SCAD_API res_T
-scad_remove
- (scad_geom_T geom);
+scad_scene_create_group
+ (struct scad_scene* scene,
+ const char* name, /* Can be NULL */
+ const int dim,
+ struct scad_geometry** geometries,
+ const size_t count,
+ struct scad_geometry** group);
SCAD_API res_T
-scad_concat
- (scad_geom_T* geom1,
- const scad_geom_T geom2);
+scad_scene_conformal_mesh
+ (struct scad_scene* scene);
+/* Compute the boolean union (the fusion) of the geometries `geom1' and `geom2'.
+ * Remove geom1 and geom2 from scene if `remove' is set. */
SCAD_API res_T
-scad_fuse
- (const scad_geom_T geom1,
- const scad_geom_T geom2,
- scad_geom_T* out,
+scad_scene_fuse_geometries
+ (struct scad_scene* scene,
+ const char* name, /* Can be NULL */
+ struct scad_geometry* geom1,
+ struct scad_geometry* geom2,
+ struct scad_geometry** out_geometry,
const int remove);
+/* Compute the boolean difference between the geometries `geom1' and `geom2'.
+ * Remove geom1 and geom2 from scene if `remove' is set. */
SCAD_API res_T
-scad_cut
- (const scad_geom_T geom1,
- const scad_geom_T geom2,
- scad_geom_T* out,
+scad_scene_cut_geometries
+ (struct scad_scene* scene,
+ const char* name, /* Can be NULL */
+ struct scad_geometry* geom1,
+ struct scad_geometry* geom2,
+ struct scad_geometry** out_geometry,
const int remove);
+/* Compute the boolean intersection (the common parts) of the geometries
+ * `geom1' and `geom2'.
+ * Remove geom1 and geom2 from scene if `remove' is set. */
SCAD_API res_T
-scad_intersect
- (const scad_geom_T geom1,
- const scad_geom_T geom2,
- scad_geom_T* out,
+scad_scene_intersect_geometries
+ (struct scad_scene* scene,
+ const char* name, /* Can be NULL */
+ struct scad_geometry* geom1,
+ struct scad_geometry* geom2,
+ struct scad_geometry** out_geometry,
const int remove);
+/* compute boundary intersection (the common part) of geom1 and geom2
+ * Remove geom1 and geom2 from scene if `remove' is set. */
SCAD_API res_T
-scad_fragment
- (const scad_geom_T geom1,
- const scad_geom_T geom2,
- scad_geom_T* out,
+scad_scene_geometries_common_boundaries
+ (struct scad_scene* scene,
+ const char* name, /* Can be NULL */
+ struct scad_geometry* geom1,
+ struct scad_geometry* geom2,
+ struct scad_geometry** out_geometry,
const int remove);
+/* Compute the boolean fragments (general fuse) resulting from the
+ * intersection of the geometries `geom1' and `geom2', making all iterfaces
+ * conformal. 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.
+ * Remove geom1 and geom2 from scene if `remove' is set. */
SCAD_API res_T
-scad_boundary
- (const scad_geom_T geom1,
- scad_geom_T* out);
+scad_scene_geometries_fragment
+ (struct scad_scene* scene,
+ const char* name, /* Can be NULL */
+ struct scad_geometry* geom1,
+ struct scad_geometry* geom2,
+ struct scad_geometry** out_geometry,
+ const int remove);
+
+/* Get the boundary of the geometry `geom'. */
+SCAD_API res_T
+scad_scene_geometry_boundary
+ (struct scad_scene* scene,
+ const char* name, /* Can be NULL */
+ struct scad_geometry* geom,
+ struct scad_geometry** out);
+/* Translate the geometry `geom' along (`dx', `dy', `dz'). */
SCAD_API res_T
-scad_translate
- (scad_geom_T geom,
+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_rotate
- (scad_geom_T geom,
+scad_geometry_rotate
+ (struct scad_geometry* geom,
const double pt[3],
- const double axis[3],
+ const double dir[3],
const double angle);
-SCAD_API res_T
-scad_conformal_mesh
- (void);
-
+/* 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
- (const scad_geom_T geom,
- char* prefix);
+ (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_stl_export_split
- (const scad_geom_T geom,
- char* prefix);
+ (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 /* FIXME remove this */
+scad_run_ui
+ (void);
END_DECLS
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,197 @@
+/* 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_c.h"
+#include "scad_device.h"
+#include "scad_scene.h"
+
+#include <rsys/logger.h>
+#include <rsys/mem_allocator.h>
+#include <rsys/ref_count.h>
+#include <rsys/cstr.h>
+
+#include <gmshc.h>
+
+/* Count of devices */
+static size_t device_count = 0;
+
+/*******************************************************************************
+ * Local functions
+ ******************************************************************************/
+static void
+device_release(ref_T* ref)
+{
+ struct scad_device* dev;
+ int ierr = 0;
+ ASSERT(ref);
+ dev = CONTAINER_OF(ref, struct scad_device, ref);
+ MEM_RM(dev->allocator, dev);
+ if(--device_count == 0) {
+ gmshFinalize(&ierr);
+ ASSERT(!ierr);
+ }
+}
+
+void
+log_error(struct scad_device* dev, const char* msg, ...)
+{
+ va_list vargs_list;
+ ASSERT(dev && msg);
+
+ 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);
+
+ va_start(vargs_list, msg);
+ log_msg(dev, LOG_WARNING, msg, vargs_list);
+ va_end(vargs_list);
+}
+
+res_T
+device_set_current_scene(struct scad_scene* scene)
+{
+ int ierr = 0;
+ ASSERT(scene);
+ if(scene->device->current_scene == scene) return RES_OK;
+ gmshModelSetCurrent(scene->name, &ierr);
+ if(ierr) scene->device->current_scene = NULL;
+ else scene->device->current_scene = scene;
+ return gmsh_err_to_res_T(ierr);
+}
+
+/*******************************************************************************
+ * Exported scad_device functions
+ ******************************************************************************/
+res_T
+scad_device_create
+ (struct logger* logger,
+ struct mem_allocator* mem_allocator,
+ const int verbose,
+ struct scad_device** out_dev)
+{
+ struct scad_device* dev = NULL;
+ struct mem_allocator* allocator;
+ /* Current gmsh implementation doesn't allow multiple devices
+ * Doesn't even define the device concept */
+ int ierr = 0;
+ res_T res = RES_OK;
+
+ if(!out_dev) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ if(device_count != 0) {
+ if(verbose && logger) {
+ res = logger_print(logger, LOG_ERROR,
+ "scad_device_create can only be called once in a process lifetime.");
+ ASSERT(res == RES_OK);
+ }
+ res = RES_BAD_OP;
+ goto error;
+ }
+
+ if(device_count == 0) {
+ gmshInitialize(0, NULL, 1, 0, &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+ }
+
+ allocator = mem_allocator ? mem_allocator : &mem_default_allocator;
+ dev = (struct scad_device*)MEM_CALLOC(allocator, 1, sizeof(struct scad_device));
+ if(!dev) {
+ res = RES_MEM_ERR;
+ goto error;
+ }
+ dev->logger = logger ? logger : LOGGER_DEFAULT;
+ dev->allocator = allocator;
+ dev->verbose = verbose;
+ ref_init(&dev->ref);
+ device_count++;
+ /* Init to default */
+ scad_device_set_params(dev, NULL);
+
+exit:
+ if(out_dev) *out_dev = dev;
+ return res;
+error:
+ if(dev) {
+ SCAD(device_ref_put(dev));
+ dev = NULL;
+ }
+ goto exit;
+}
+
+res_T
+scad_device_set_params
+ (struct scad_device* device,
+ const struct scad_device_param* params)
+{
+ res_T res = RES_OK;
+ const struct scad_device_param* actual_params
+ = params ? params : &SCAD_INIT_DEFAULT;
+ int ierr = 0;
+
+ if(!device) {
+ res= RES_BAD_ARG;
+ goto error;
+ }
+
+ gmshOptionSetNumber("Mesh.StlOneSolidPerSurface",
+ actual_params->Mesh.StlOneSolidPerSurface, &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+ gmshOptionSetNumber("Mesh.MeshSizeFromPoints",
+ actual_params->Mesh.MeshSizeFromPoints, &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+ gmshOptionSetNumber("Mesh.MeshSizeFromCurvature",
+ actual_params->Mesh.MeshSizeFromCurvature, &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+ gmshOptionSetNumber("Mesh.MinimumElementsPerTwoPi",
+ actual_params->Mesh.MinimumElementsPerTwoPi, &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+ gmshOptionSetNumber("Mesh.MeshSizeExtendFromBoundary",
+ actual_params->Mesh.MeshSizeExtendFromBoundary, &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+
+exit:
+ return res;
+error:
+ fprintf(stderr, "%s: can't initialize gmsh -- %s\n",
+ FUNC_NAME, res_to_cstr(res));
+ goto exit;
+}
+
+res_T
+scad_device_ref_get(struct scad_device* dev)
+{
+ if(!dev) return RES_BAD_ARG;
+ ref_get(&dev->ref);
+ return RES_OK;
+}
+
+res_T
+scad_device_ref_put(struct scad_device* dev)
+{
+ if(!dev) return RES_BAD_ARG;
+ ref_put(&dev->ref, device_release);
+ return RES_OK;
+}
diff --git a/src/scad_device.h b/src/scad_device.h
@@ -0,0 +1,82 @@
+/* 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>
+
+struct scad_device {
+ struct logger* logger;
+ struct mem_allocator* allocator;
+ struct scad_scene* current_scene;
+ unsigned next_scene_id;
+ 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
+;
+
+static INLINE void
+log_msg
+ (struct scad_device* dev,
+ const enum log_type stream,
+ const char* msg,
+ va_list vargs)
+{
+ ASSERT(dev && msg);
+ if(dev->verbose) {
+ res_T res; (void)res;
+ res = logger_vprint(dev->logger, stream, msg, vargs);
+ ASSERT(res == RES_OK);
+ }
+}
+
+extern LOCAL_SYM res_T
+device_set_current_scene(struct scad_scene* scene);
+
+#endif
diff --git a/src/scad_geometry.c b/src/scad_geometry.c
@@ -0,0 +1,764 @@
+/* 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_c.h"
+#include "scad_scene.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 <stdlib.h>
+#include <gmshc.h>
+
+/*******************************************************************************
+ * Utility functions
+ ******************************************************************************/
+static res_T
+scad_geometry_create
+ (struct scad_scene* scene,
+ const char* name,
+ const unsigned scad_ID,
+ struct scad_geometry** out_geometry)
+{
+ res_T res = RES_OK;
+ struct scad_geometry* geom = NULL;
+
+ ASSERT(scene && out_geometry);
+ if(name && htable_names_find(&scene->geometry_names, &name)) {
+ /* if defined, names must be unique */
+ res = RES_BAD_ARG;
+ goto error;
+ }
+ geom = (struct scad_geometry*)MEM_CALLOC(scene->device->allocator, 1,
+ sizeof(*geom));
+ if(!geom) {
+ res = RES_MEM_ERR;
+ goto error;
+ }
+
+ str_init(scene->device->allocator, &geom->name);
+ geom->scene = scene;
+ geom->scad_ID = scad_ID;
+ scene->device->need_synchro = 1;
+ ASSERT(!htable_geometries_find(&scene->attached_geometry, &geom->scad_ID));
+ ERR(htable_geometries_set(&scene->attached_geometry, &geom->scad_ID,
+ &geom));
+ if(name) {
+ ERR(str_set(&geom->name, name));
+ ERR(htable_names_set(&scene->geometry_names, &name, &geom));
+ }
+
+end:
+ if(out_geometry) *out_geometry = geom;
+ return res;
+error:
+ if(geom) {
+ SCAD(geometry_release(geom));
+ geom = NULL;
+ }
+ goto end;
+}
+
+
+static void
+remove_dimTags
+ (struct scad_geometry* geom)
+{
+ ASSERT(geom);
+ geom->gmsh_dimTags_n = 0;
+ if(geom->gmsh_dimTags) free(geom->gmsh_dimTags);
+ geom->gmsh_dimTags = NULL;
+}
+
+/*******************************************************************************
+ * Local functions
+ ******************************************************************************/
+res_T
+scad_geometry_release
+ (struct scad_geometry* geom)
+{
+ struct mem_allocator* allocator;
+ size_t sz;
+ int* data;
+ int ierr;
+ res_T res = RES_OK;
+ ASSERT(geom);
+ allocator = geom->scene->device->allocator;
+ geom->scene->device->need_synchro = 1;
+
+ sz = geom->gmsh_dimTags_n;
+ data = geom->gmsh_dimTags;
+ if(geom->is_group) {
+ int dimTag[2];
+ dimTag[0] = geom->group_dim;
+ dimTag[1] = geom->is_group;
+ /* FIXME: not sure this remove is needed! */
+ gmshModelGeoRemovePhysicalGroups(dimTag, 2, &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+ if(sz != 0) {
+ ASSERT(data);
+ free(data);
+ }
+ }
+ else if(sz != 0) {
+ gmshModelOccRemove(data, sz, 0, &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+ ASSERT(data);
+ free(data);
+ }
+ str_release(&geom->name);
+ MEM_RM(allocator, geom);
+
+end:
+ return res;
+error:
+ goto end;
+}
+
+/******************************************************************************
+ * Exported functions
+ *****************************************************************************/
+res_T
+scad_scene_add_box
+ (struct scad_scene* scene,
+ const char* name,
+ const double xyz[3],
+ const double dxdydz[3],
+ struct scad_geometry** out_geometry)
+{
+ int ierr, gmsh_ID;
+ unsigned scad_ID;
+ struct scad_geometry* geom = NULL;
+ res_T res = RES_OK;
+
+ if(!scene || !xyz || !dxdydz) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ ERR(device_set_current_scene(scene));
+ gmsh_ID = gmshModelOccAddBox(SPLIT3(xyz), SPLIT3(dxdydz), -1, &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+
+ scad_ID = scene->current_ID++;
+ ERR(scad_geometry_create(scene, name, scad_ID, &geom));
+ geom->gmsh_dimTags_n = 2;
+ geom->gmsh_dimTags = malloc(geom->gmsh_dimTags_n * sizeof(int));
+ if(! geom->gmsh_dimTags_n) {
+ res = RES_MEM_ERR;
+ goto error;
+ }
+ geom->gmsh_dimTags[0] = 3;
+ geom->gmsh_dimTags[1] = gmsh_ID;
+
+exit:
+ if(out_geometry) *out_geometry = geom;
+ return res;
+error:
+ if(geom) {
+ SCAD(geometry_release(geom));
+ geom = NULL;
+ }
+ goto exit;
+}
+
+res_T
+scad_scene_add_cylinder
+ (struct scad_scene* scene,
+ const char* name,
+ const double xyz[3],
+ const double axis[3],
+ const double rad,
+ const double angle,
+ struct scad_geometry** out_geometry)
+{
+ int ierr, gmsh_ID;
+ unsigned scad_ID;
+ struct scad_geometry* geom = NULL;
+ res_T res = RES_OK;
+
+ if(!scene || !xyz || !axis || rad <= 0 || angle < 0 || angle > 2*PI) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ ERR(device_set_current_scene(scene));
+ gmsh_ID = gmshModelOccAddCylinder(SPLIT3(xyz), SPLIT3(axis), rad, -1, angle,
+ &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+
+ scad_ID = scene->current_ID++;
+ ERR(scad_geometry_create(scene, name, scad_ID, &geom));
+ geom->gmsh_dimTags_n = 2;
+ geom->gmsh_dimTags = malloc(geom->gmsh_dimTags_n * sizeof(int));
+ if(! geom->gmsh_dimTags_n) {
+ res = RES_MEM_ERR;
+ goto error;
+ }
+ geom->gmsh_dimTags[0] = 3;
+ geom->gmsh_dimTags[1] = gmsh_ID;
+
+exit:
+ if(out_geometry) *out_geometry = geom;
+ return res;
+error:
+ if(geom) {
+ SCAD(geometry_release(geom));
+ geom = NULL;
+ }
+ goto exit;
+}
+
+int
+scad_scene_add_sphere
+ (struct scad_scene* scene,
+ const char* name,
+ const double xyz[3],
+ const double rad,
+ struct scad_geometry** out_geometry)
+{
+ int ierr, gmsh_ID;
+ unsigned scad_ID;
+ struct scad_geometry* geom = NULL;
+ res_T res = RES_OK;
+
+ if(!scene || !xyz || rad <= 0) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ ERR(device_set_current_scene(scene));
+ gmsh_ID = gmshModelOccAddSphere(SPLIT3(xyz), rad, -1, -PI/2, PI/2, 2*PI, &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+
+ scad_ID = scene->current_ID++;
+ ERR(scad_geometry_create(scene, name, scad_ID, &geom));
+ geom->gmsh_dimTags_n = 2;
+ geom->gmsh_dimTags = malloc(geom->gmsh_dimTags_n * sizeof(int));
+ if(! geom->gmsh_dimTags_n) {
+ res = RES_MEM_ERR;
+ goto error;
+ }
+ geom->gmsh_dimTags[0] = 3;
+ geom->gmsh_dimTags[1] = gmsh_ID;
+
+exit:
+ if(out_geometry) *out_geometry = geom;
+ return res;
+error:
+ if(geom) {
+ SCAD(geometry_release(geom));
+ geom = NULL;
+ }
+ goto exit;
+}
+
+res_T
+scad_scene_create_group
+ (struct scad_scene* scene,
+ const char* name,
+ const int dim,
+ struct scad_geometry** geometries,
+ const size_t count,
+ struct scad_geometry** out_geometry)
+{
+ int ierr, gid;
+ struct scad_geometry* geom = NULL;
+ unsigned scad_id;
+ size_t sz, i, n;
+ int* ids = NULL;
+ res_T res = RES_OK;
+
+ if(!scene || !geometries || (count == 0) || !out_geometry) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ if(scene->device->need_synchro) {
+ ERR(scad_device_synchronize(scene->device));
+ }
+
+ sz = 0;
+ for(i = 0; i < count; i++) {
+ sz += geometries[i]->gmsh_dimTags_n / 2;
+ }
+ ids = malloc(sz * sizeof(int));
+ if(! ids) {
+ res = RES_MEM_ERR;
+ goto error;
+ }
+ n = 0;
+ for(i = 0; i < count; i++) {
+ size_t j;
+ for(j = 1; j < geometries[i]->gmsh_dimTags_n; j+=2) {
+ ids[n++] = geometries[i]->gmsh_dimTags[j];
+ }
+ }
+ ASSERT(n == sz);
+ ERR(device_set_current_scene(scene));
+ gid = gmshModelAddPhysicalGroup(dim, ids, count, -1, &ierr);
+ ASSERT(gid > 0);
+ ERR(gmsh_err_to_res_T(ierr));
+
+ scad_id = scene->current_ID++;
+ ERR(scad_geometry_create(scene, name, scad_id, &geom));
+ geom->is_group = gid;
+ geom->group_dim = dim;
+ geom->gmsh_dimTags_n = 2 * sz;
+ geom->gmsh_dimTags = malloc(geom->gmsh_dimTags_n * sizeof(int));
+ if(! geom->gmsh_dimTags_n) {
+ res = RES_MEM_ERR;
+ goto error;
+ }
+ n = 0;
+ for(i = 0; i < count; i++) {
+ size_t j;
+ for(j = 0; j < geometries[i]->gmsh_dimTags_n; j++) {
+ geom->gmsh_dimTags[n++] = geometries[i]->gmsh_dimTags[j];
+ }
+ }
+ ASSERT(n == geom->gmsh_dimTags_n);
+
+exit:
+ if(out_geometry) *out_geometry = geom;
+ if(ids) free(ids);
+ return res;
+error:
+ if(geom) {
+ SCAD(geometry_release(geom));
+ geom = NULL;
+ }
+ goto exit;
+}
+
+res_T
+scad_scene_fuse_geometries
+ (struct scad_scene* scene,
+ const char* name,
+ struct scad_geometry* geom1,
+ struct scad_geometry* geom2,
+ struct scad_geometry** out_geometry,
+ const int remove)
+{
+ int* tagout = NULL;
+ int** map = NULL;
+ size_t* mapn = NULL;
+ size_t tagoutn, mapnn, sz1, sz2;
+ int* data1;
+ int* data2;
+ int ierr = 0;
+ unsigned scad_id;
+ struct scad_geometry* geom = NULL;
+ res_T res = RES_OK;
+
+ if(!scene || !geom1 || !geom2 || !out_geometry
+ || geom1->scene != scene || geom2->scene != scene)
+ {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ if(scene->device->need_synchro) {
+ ERR(scad_device_synchronize(scene->device));
+ }
+
+ sz1 = geom1->gmsh_dimTags_n;
+ sz2 = geom2->gmsh_dimTags_n;
+ data1 = geom1->gmsh_dimTags;
+ data2 = geom2->gmsh_dimTags;
+ gmshModelOccFuse(data1, sz1, data2, sz2, &tagout, &tagoutn, &map, &mapn,
+ &mapnn, -1, remove, remove, &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+
+ scad_id = scene->current_ID++;
+ ERR(scad_geometry_create(scene, name, scad_id, &geom));
+ geom->gmsh_dimTags_n = tagoutn;
+ geom->gmsh_dimTags = tagout;
+ if(remove) { /* FIXME: not sure of this! */
+ remove_dimTags(geom1);
+ remove_dimTags(geom2);
+ }
+
+exit:
+ if(out_geometry) *out_geometry = geom;
+ if(mapn) free(mapn);
+ if(map) free(map);
+ return res;
+error:
+ if(geom) {
+ SCAD(geometry_release(geom));
+ geom = NULL;
+ }
+ goto exit;
+}
+
+res_T
+scad_scene_cut_geometries
+ (struct scad_scene* scene,
+ const char* name, /* Can be NULL */
+ struct scad_geometry* geom1,
+ struct scad_geometry* geom2,
+ struct scad_geometry** out_geometry,
+ const int remove)
+{
+ int* tagout = NULL;
+ int** map = NULL;
+ size_t* mapn = NULL;
+ size_t tagoutn, mapnn, sz1, sz2;
+ int* data1;
+ int* data2;
+ int ierr = 0;
+ unsigned scad_id;
+ struct scad_geometry* geom = NULL;
+ res_T res = RES_OK;
+
+ if(!scene || !geom1 || !geom2 || !out_geometry
+ || geom1->scene != scene || geom2->scene != scene)
+ {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ if(scene->device->need_synchro) {
+ ERR(scad_device_synchronize(scene->device));
+ }
+
+ sz1 = geom1->gmsh_dimTags_n;
+ sz2 = geom2->gmsh_dimTags_n;
+ data1 = geom1->gmsh_dimTags;
+ data2 = geom2->gmsh_dimTags;
+ gmshModelOccCut(data1, sz1, data2, sz2, &tagout, &tagoutn, &map, &mapn,
+ &mapnn, -1, remove, remove, &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+
+ scad_id = scene->current_ID++;
+ ERR(scad_geometry_create(scene, name, scad_id, &geom));
+ geom->gmsh_dimTags_n = tagoutn;
+ geom->gmsh_dimTags = tagout;
+ if(remove) { /* FIXME: not sure of this! */
+ remove_dimTags(geom1);
+ remove_dimTags(geom2);
+ }
+
+exit:
+ if(out_geometry) *out_geometry = geom;
+ if(mapn) free(mapn);
+ if(map) free(map);
+ return res;
+error:
+ if(geom) {
+ SCAD(geometry_release(geom));
+ geom = NULL;
+ }
+ goto exit;
+}
+
+res_T
+scad_scene_intersect_geometries
+ (struct scad_scene* scene,
+ const char* name, /* Can be NULL */
+ struct scad_geometry* geom1,
+ struct scad_geometry* geom2,
+ struct scad_geometry** out_geometry,
+ const int remove)
+{
+ int* tagout = NULL;
+ int** map = NULL;
+ size_t* mapn = NULL;
+ size_t tagoutn, mapnn, sz1, sz2;
+ int* data1;
+ int* data2;
+ int ierr = 0;
+ unsigned scad_id;
+ struct scad_geometry* geom = NULL;
+ res_T res = RES_OK;
+
+ if(!scene || !geom1 || !geom2 || !out_geometry
+ || geom1->scene != scene || geom2->scene != scene)
+ {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ if(scene->device->need_synchro) {
+ ERR(scad_device_synchronize(scene->device));
+ }
+
+ sz1 = geom1->gmsh_dimTags_n;
+ sz2 = geom2->gmsh_dimTags_n;
+ data1 = geom1->gmsh_dimTags;
+ data2 = geom2->gmsh_dimTags;
+ gmshModelOccIntersect(data1, sz1, data2, sz2, &tagout, &tagoutn, &map, &mapn,
+ &mapnn, -1, remove, remove, &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+
+ scad_id = scene->current_ID++;
+ ERR(scad_geometry_create(scene, name, scad_id, &geom));
+ geom->gmsh_dimTags_n = tagoutn;
+ geom->gmsh_dimTags = tagout;
+ if(remove) { /* FIXME: not sure of this! */
+ remove_dimTags(geom1);
+ remove_dimTags(geom2);
+ }
+
+exit:
+ if(out_geometry) *out_geometry = geom;
+ if(mapn) free(mapn);
+ if(map) free(map);
+ return res;
+error:
+ if(geom) {
+ SCAD(geometry_release(geom));
+ geom = NULL;
+ }
+ goto exit;
+}
+
+res_T
+scad_scene_geometries_common_boundaries
+ (struct scad_scene* scene,
+ const char* name, /* Can be NULL */
+ struct scad_geometry* geom1,
+ struct scad_geometry* geom2,
+ struct scad_geometry** out_geometry,
+ const int remove)
+{
+ int* tagout = NULL;
+ int** map = NULL;
+ size_t* mapn = NULL;
+ size_t tagoutn, mapnn, sz1, sz2;
+ int* data1;
+ int* data2;
+ int ierr = 0;
+ unsigned scad_id;
+ int* bound1;
+ int* bound2;
+ size_t n1, n2;
+ struct scad_geometry* geom = NULL;
+ res_T res = RES_OK;
+
+ if(!scene || !geom1 || !geom2 || !out_geometry
+ || geom1->scene != scene || geom2->scene != scene)
+ {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ if(scene->device->need_synchro) {
+ ERR(scad_device_synchronize(scene->device));
+ }
+
+ sz1 = geom1->gmsh_dimTags_n;
+ sz2 = geom2->gmsh_dimTags_n;
+ data1 = geom1->gmsh_dimTags;
+ data2 = geom2->gmsh_dimTags;
+ 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));
+
+ scad_id = scene->current_ID++;
+ ERR(scad_geometry_create(scene, name, scad_id, &geom));
+ geom->gmsh_dimTags_n = tagoutn;
+ geom->gmsh_dimTags = tagout;
+ if(remove) { /* FIXME: not sure of this! */
+ remove_dimTags(geom1);
+ remove_dimTags(geom2);
+ }
+
+exit:
+ if(out_geometry) *out_geometry = geom;
+ if(bound1) free(bound1);
+ if(bound2) free(bound2);
+ if(mapn) free(mapn);
+ if(map) free(map);
+ return res;
+error:
+ if(geom) {
+ SCAD(geometry_release(geom));
+ geom = NULL;
+ }
+ 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;
+ }
+
+ if(geom->scene->device->need_synchro) {
+ ERR(scad_device_synchronize(geom->scene->device));
+ }
+
+ sz = geom->gmsh_dimTags_n;
+ data = geom->gmsh_dimTags;
+ gmshModelOccRotate(data, sz, SPLIT3(pt), SPLIT3(axis), angle, &ierr);
+ geom->scene->device->need_synchro = 1;
+ ERR(gmsh_err_to_res_T(ierr));
+
+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;
+ }
+
+ if(geom->scene->device->need_synchro) {
+ ERR(scad_device_synchronize(geom->scene->device));
+ }
+
+ sz = geom->gmsh_dimTags_n;
+ data = geom->gmsh_dimTags;
+ gmshModelOccTranslate(data, sz, SPLIT3(dxdydz), &ierr);
+ geom->scene->device->need_synchro = 1;
+ ERR(gmsh_err_to_res_T(ierr));
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+res_T
+scad_scene_geometries_fragment
+ (struct scad_scene* scene,
+ const char* name,
+ struct scad_geometry* geom1,
+ struct scad_geometry* geom2,
+ struct scad_geometry** out_geometry,
+ const int remove)
+{
+ int* tagout = NULL;
+ int** map = NULL;
+ size_t* mapn = NULL;
+ size_t tagoutn, mapnn, sz1, sz2;
+ int* data1;
+ int* data2;
+ int ierr = 0;
+ unsigned scad_id;
+ struct scad_geometry* geom = NULL;
+ res_T res = RES_OK;
+
+ if(!scene || !geom1 || !geom2 || !out_geometry
+ || geom1->scene != scene || geom2->scene != scene)
+ {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ if(scene->device->need_synchro) {
+ ERR(scad_device_synchronize(scene->device));
+ }
+
+ sz1 = geom1->gmsh_dimTags_n;
+ sz2 = geom2->gmsh_dimTags_n;
+ data1 = geom1->gmsh_dimTags;
+ data2 = geom2->gmsh_dimTags;
+ gmshModelOccFragment(data1, sz1, data2, sz2, &tagout, &tagoutn, &map, &mapn,
+ &mapnn, -1, remove, remove, &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+
+ scad_id = scene->current_ID++;
+ ERR(scad_geometry_create(scene, name, scad_id, &geom));
+ geom->gmsh_dimTags_n = tagoutn;
+ geom->gmsh_dimTags = tagout;
+ if(remove) { /* FIXME: not sure of this! */
+ remove_dimTags(geom1);
+ remove_dimTags(geom2);
+ }
+
+exit:
+ if(out_geometry) *out_geometry = geom;
+ if(mapn) free(mapn);
+ if(map) free(map);
+ return res;
+error:
+ goto exit;
+}
+
+res_T
+scad_scene_geometry_boundary
+ (struct scad_scene* scene,
+ const char* name,
+ struct scad_geometry* geom,
+ struct scad_geometry** out_geometry)
+{
+ int* tagout = NULL;
+size_t tagoutn, sz;
+ int* data;
+ int ierr = 0;
+ unsigned scad_id;
+ res_T res = RES_OK;
+
+ if(!scene || !geom || !out_geometry || geom->scene != scene) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ if(scene->device->need_synchro) {
+ ERR(scad_device_synchronize(scene->device));
+ }
+
+ sz = geom->gmsh_dimTags_n;
+ data = geom->gmsh_dimTags;
+ gmshModelGetBoundary(data, sz, &tagout, &tagoutn, 1, 0, 0, &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+
+ scad_id = scene->current_ID++;
+ ERR(scad_geometry_create(scene, name, scad_id, &geom));
+ geom->gmsh_dimTags_n = tagoutn;
+ geom->gmsh_dimTags = tagout;
+
+exit:
+ if(out_geometry) *out_geometry = geom;
+ return res;
+error:
+ goto exit;
+}
+
diff --git a/src/scad_geometry.h b/src/scad_geometry.h
@@ -0,0 +1,39 @@
+/* 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>
+
+struct scad_scene;
+
+struct scad_geometry {
+ int* gmsh_dimTags;
+ size_t gmsh_dimTags_n;
+ struct scad_scene* scene;
+ struct str name;
+ unsigned scad_ID;
+ int is_group, group_dim;
+};
+
+extern LOCAL_SYM res_T
+scad_geometry_release
+ (struct scad_geometry* geom);
+
+#endif
diff --git a/src/scad_scene.c b/src/scad_scene.c
@@ -0,0 +1,172 @@
+/* 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 /* snprintf */
+
+#include "scad.h"
+#include "scad_c.h"
+#include "scad_scene.h"
+#include "scad_device.h"
+
+#include <rsys/rsys.h>
+
+#include <stdio.h>
+#include <gmshc.h>
+
+/*******************************************************************************
+ * Helper functions
+ ******************************************************************************/
+static res_T
+set_name
+ (struct scad_scene* scene)
+{
+ int n;
+ ASSERT(scene);
+ n = snprintf(scene->name, sizeof(scene->name), "%x",
+ scene->device->next_scene_id++);
+ if((size_t)n >= sizeof(scene->name)) return RES_BAD_ARG;
+ return RES_OK;
+}
+
+static void
+scene_release(ref_T* ref)
+{
+ struct scad_scene* scn;
+ struct mem_allocator* allocator;
+ struct htable_geometries_iterator it, end;
+ int ierr = 0;
+ res_T res;
+ ASSERT(ref);
+ scn = CONTAINER_OF(ref, struct scad_scene, ref);
+ allocator = scn->device->allocator;
+ htable_geometries_begin(&scn->attached_geometry, &it);
+ htable_geometries_end(&scn->attached_geometry, &end);
+ while(!htable_geometries_iterator_eq(&it, &end)) {
+ struct scad_geometry* geom = *htable_geometries_iterator_data_get(&it);
+ ASSERT(geom->scene == scn);
+ SCAD(geometry_release(geom));
+ htable_geometries_iterator_next(&it);
+ }
+ htable_geometries_release(&scn->attached_geometry);
+ htable_names_release(&scn->geometry_names);
+ res = device_set_current_scene(scn);
+ ASSERT(res == RES_OK); (void)res;
+ gmshModelRemove(&ierr);
+ ASSERT(!ierr); (void)ierr;
+ scn->device->current_scene = NULL;
+ SCAD(device_ref_put(scn->device));
+ MEM_RM(allocator, scn);
+}
+
+/*******************************************************************************
+ * Local functions
+ ******************************************************************************/
+
+/*******************************************************************************
+ * Exported scad_scene functions
+ ******************************************************************************/
+res_T
+scad_scene_create
+ (struct scad_device* device,
+ struct scad_scene** out_scene)
+{
+ struct scad_scene* scn = NULL;
+ int ierr = 0;
+ res_T res = RES_OK;
+
+ if(!out_scene || !device) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ scn = (struct scad_scene*)MEM_CALLOC(device->allocator, 1,
+ sizeof(struct scad_scene));
+ if(!scn) {
+ res = RES_MEM_ERR;
+ goto error;
+ }
+ htable_geometries_init(device->allocator, &scn->attached_geometry);
+ htable_names_init(device->allocator, &scn->geometry_names);
+ scn->device = device;
+ set_name(scn);
+ ref_init(&scn->ref);
+ SCAD(device_ref_get(scn->device));
+
+ gmshModelAdd(scn->name, &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+
+ device->current_scene = scn;
+
+exit:
+ if(out_scene) *out_scene = scn;
+ return res;
+error:
+ if(scn) {
+ SCAD(scene_ref_put(scn));
+ scn = NULL;
+ }
+ goto exit;
+}
+
+res_T
+scad_scene_conformal_mesh
+ (struct scad_scene* scene)
+{
+ int* dimTags = NULL;
+ size_t dimTags_n;
+ int ierr = 0;
+ res_T res = RES_OK;
+
+ scene->device->need_synchro = 1;
+ device_set_current_scene(scene);
+ gmshModelOccSynchronize(&ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+ gmshModelOccGetEntities(&dimTags, &dimTags_n, 3, &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+ if(dimTags_n > 2) {
+ /* Remove all duplicate entities in the OpenCASCADE CAD representation
+ * (different entities at the same geometrical location) after intersecting
+ * (using boolean fragments) all highest dimensional entities. */
+ gmshModelOccRemoveAllDuplicates(&ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+ }
+ gmshModelOccSynchronize(&ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+ gmshModelMeshGenerate(2, &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+
+exit:
+ if(dimTags) free(dimTags);
+ return res;
+error:
+ goto exit;
+}
+
+
+res_T
+scad_scene_ref_get(struct scad_scene* scene)
+{
+ if(!scene) return RES_BAD_ARG;
+ ref_get(&scene->ref);
+ return RES_OK;
+}
+
+res_T
+scad_scene_ref_put(struct scad_scene* scene)
+{
+ if(!scene) return RES_BAD_ARG;
+ ref_put(&scene->ref, scene_release);
+ return RES_OK;
+}
diff --git a/src/scad_scene.h b/src/scad_scene.h
@@ -0,0 +1,55 @@
+/* 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_SCENE_H
+#define SCAD_SCENE_H
+
+#include "scad.h"
+
+struct scad_device;
+struct scad_geometry;
+
+#include <rsys/ref_count.h>
+#include <rsys/hash_table.h>
+
+#define HTABLE_NAME geometries
+#define HTABLE_DATA struct scad_geometry*
+#define HTABLE_KEY unsigned /* scad ID */
+#include <rsys/hash_table.h>
+
+static INLINE char
+name_eq(char* const* a, char* const* b)
+{
+ ASSERT(a && b);
+ return strcmp(*a, *b) == 0;
+}
+
+#define HTABLE_NAME names
+#define HTABLE_DATA struct scad_geometry*
+#define HTABLE_KEY const char*
+#define HTABLE_KEY_FUNCTOR_EQ name_eq
+#include <rsys/hash_table.h>
+
+struct scad_scene {
+ struct scad_device* device;
+ struct htable_geometries attached_geometry;
+ struct htable_names geometry_names;
+ char name[8];
+ unsigned current_ID;
+
+ ref_T ref;
+};
+
+#endif
diff --git a/src/test1.c b/src/test1.c
@@ -14,8 +14,26 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include "scad.h"
+#include "scad_geometry.h"
-#define ERR(Expr) if((res = (Expr)) != RES_OK) goto error; else (void)0
+#include <rsys/rsys.h>
+#include <rsys/math.h>
+#include <rsys/mem_allocator.h>
+
+#include <stdlib.h>
+
+#define OK(Expr) if((res = (Expr)) != RES_OK) goto error; else (void)0
+#define BAD(Expr) if((res = (Expr)) != RES_BAD_ARG) goto error; else (void)0
+
+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");
+ }
+}
int
main(int argc, char* argv[])
@@ -25,28 +43,55 @@ main(int argc, char* argv[])
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;
+ struct scad_geometry* geom1 = NULL;
+ struct scad_geometry* geom2 = NULL;
+ struct scad_geometry* cyl = NULL;
+ struct scad_geometry* group1 = NULL;
+ struct scad_geometry* f1 = NULL;
+ struct scad_geometry* f2 = NULL;
+ struct scad_geometry* sphere = NULL;
+ struct scad_geometry* geoms[2];
+ struct scad_device* dev = NULL;
+ struct scad_scene* scene = NULL;
+ struct mem_allocator allocator;
(void)argc; (void)argv;
- ERR(scad_init());
- ERR(scad_addbox(p1, d1, &box1));
- ERR(scad_addbox(p2, d2, &box2));
+ OK(mem_init_proxy_allocator(&allocator, &mem_default_allocator));
+ OK(scad_device_create(NULL, &allocator, 1, &dev));
+ OK(scad_scene_create(dev, &scene));
+
+ OK(scad_scene_add_cylinder(scene, "c1", p1, d1, 2, PI, &geom1));
+ OK(scad_scene_add_box(scene, "b1", p2, d2, &geom2));
+ OK(scad_stl_export(geom1, NULL, 1));
+ OK(scad_stl_export(geom2, NULL, 1));
+ geoms[0] = geom1;
+ geoms[1] = geom2;
+ OK(scad_scene_create_group(scene, "g1", 3, geoms, 2, &group1));
+ OK(scad_stl_export(group1, NULL, 1));
- ERR(scad_conformal_mesh());
-#if 0
- ERR(scad_common_interface(box1, box2, &cut, 0/*no delete*/));
- ERR(scad_stl_export(cut, "mesh.stl"));
-#endif
+ OK(scad_scene_conformal_mesh(scene));
+ OK(scad_stl_export_split(group1, "conformal", 1));
+
+ OK(scad_scene_add_cylinder(scene, "cyl", p1, d1, 1, 2*PI, &cyl));
+ OK(scad_stl_export(cyl, NULL, 1));
+
+ OK(scad_scene_fuse_geometries(scene, "fused1", geom1, cyl, &f1, 0));
+ OK(scad_stl_export(f1, NULL, 1));
+
+ OK(scad_scene_add_sphere(scene, "s1", p1, 1, &sphere));
+ OK(scad_scene_fuse_geometries(scene, "fused2", group1, sphere, &f2, 0));
+ OK(scad_stl_export(f2, NULL, 0));
exit:
- scad_geom_release(box1);
- scad_geom_release(box2);
- scad_geom_release(cut);
- scad_release();
- return res;
+ if(dev) SCAD(device_ref_put(dev));
+ if(scene) SCAD(scene_ref_put(scene));
+
+ check_memory_allocator(&allocator);
+ mem_shutdown_proxy_allocator(&allocator);
+ CHK(mem_allocated_size() == 0);
+
+ return (res == RES_OK) ? EXIT_SUCCESS : EXIT_FAILURE;
error:
goto exit;
}