star-cad

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

commit acb875d4315820ef0da2f1daf90022a30dd914d6
parent 3e66a85c6765bcea4401c206f1310e242f97271e
Author: Christophe Coustet <christophe.coustet@meso-star.com>
Date:   Fri, 28 Apr 2023 10:12:15 +0200

Merge branch 'release_0.2'

Diffstat:
MREADME.md | 13+++++++++++++
Mcmake/CMakeLists.txt | 2+-
Msrc/scad.c | 549+++++++++++++++++++++++++++++++++++++++++++------------------------------------
Msrc/scad.h | 63++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
Msrc/scad_c.h | 8++++++++
Msrc/scad_device.c | 7++++++-
Msrc/scad_device.h | 3+++
Msrc/scad_geometry.c | 281+++++++++++++++++++++++++++++++++++++++----------------------------------------
Msrc/scad_geometry.h | 11+++++------
Msrc/test_api.c | 70+++++++++++++++++++++++++++++++++++++++++++++++-----------------------
Msrc/test_export.c | 12++++++------
Msrc/test_partition.c | 8++++----
12 files changed, 589 insertions(+), 438 deletions(-)

diff --git a/README.md b/README.md @@ -21,6 +21,19 @@ project from the `cmake/CMakeLists.txt` file by appending to the ## Release notes +### Version 0.1.1 + +- Introduce reference counting for geometries (API break). +- Add the scad_stl_data_write API call to write a vector of coordinate as an STL + file the same way scad_stl_export do (producing the same decimals/bytes). +- Add scad_stl_get_data and scad_stl_get_data_partial API calls to get the mesh + of a geometry, allowing to manipulate it before writing it. +- Add the scad_stl_rxport_partial API call to export part of a geometry to an + STL file. +- Remove the reverse arg from STL related functions (API break). +- Add a scad_geometry_reverse API call. +- Code simplifications in STL-related code + ### Version 0.1.0 Initial version. diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -37,7 +37,7 @@ include_directories(${GMSH_INCLUDE_DIR} ${RSys_INCLUDE_DIR}) # Configure and define targets ################################################################################ set(VERSION_MAJOR 0) -set(VERSION_MINOR 1) +set(VERSION_MINOR 2) set(VERSION_PATCH 0) set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}) diff --git a/src/scad.c b/src/scad.c @@ -23,26 +23,37 @@ #include <rsys/cstr.h> #include <rsys/rsys.h> #include <rsys/str.h> +#include <rsys/dynamic_array_int.h> +#include <rsys/dynamic_array_double.h> #include <rsys/double3.h> #include <rsys/float3.h> +#include <star/sstl.h> + #include <stdio.h> +#include <stdlib.h> #define OKP(Expr) if((Expr) < 0) { res=RES_IO_ERR; goto error; } +static int +int_compare(const void *a_, const void *b_) { + const int *a = (const int *)a_; + const int *b = (const int *)b_; + ASSERT(a && b); + return (*a > *b) - (*a < *b); +} + static res_T write_ascii_stl (const char* filename, - const unsigned trg_count, - double* const* coord, - const size_t* coord_n) + double* coord, + const size_t coord_n) { res_T res = RES_OK; size_t i; FILE* stl_file = NULL; - unsigned cpt; - ASSERT(filename && coord && coord_n); + ASSERT(filename && (coord || coord_n == 0) && (coord_n % 9 == 0)); stl_file = fopen(filename, "w"); if(!stl_file) { @@ -52,39 +63,32 @@ write_ascii_stl 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")); + /* trg_count triangles split in coord_n blocs */ + for(i = 0; i < coord_n; i += 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+0); + d3_set(vtx[1], coord+i+3); + d3_set(vtx[2], coord+i+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); - cpt++; + 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]))); } - i++; + OKP(fprintf(stl_file, " endloop\n")); + OKP(fprintf(stl_file, " endfacet\n")); } OKP(fprintf(stl_file, "endsolid \n")); @@ -100,17 +104,16 @@ error: static res_T write_binary_stl (const char* filename, - const unsigned trg_count, - double* const* coord, - const size_t* coord_n) + double* coord, + const size_t coord_n) { res_T res = RES_OK; size_t i; + unsigned trg_count; char header[80] = "Binary STL"; FILE* stl_file = NULL; - unsigned cpt; - ASSERT(filename && coord && coord_n); + ASSERT(filename && (coord || coord_n == 0) && (coord_n % 9 == 0)); stl_file = fopen(filename, "wb"); if(!stl_file) { @@ -123,37 +126,32 @@ write_binary_stl goto error; } + trg_count = (unsigned)coord_n / 9; if(1 != fwrite(&trg_count, 4, 1, stl_file)) { res = RES_IO_ERR; goto error; } - 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++; + /* trg_count triangles split in coord_n blocs */ + for(i = 0; i < coord_n; i += 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+0); + f3_set_d3(trg.vrtx[1], coord+i+3); + f3_set_d3(trg.vrtx[2], coord+i+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; } - i++; } exit: @@ -165,17 +163,60 @@ error: goto exit; } -static res_T -write_stl - (const char* filename, - const unsigned trg_count, - double* const* coord, - const size_t* coord_n, - const int binary) +/* Accumulate tags of geometry in tags */ +res_T +get_2d_tags + (struct scad_geometry* geometry, + struct darray_int* tags) { - 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); + int* dimTags_to_free = NULL; + size_t i; + int ierr = 0; + int* data; + size_t sz; + res_T res = RES_OK; + + ASSERT(geometry && tags); + + sz = geometry->gmsh_dimTags_n; + data = geometry->gmsh_dimTags; + ASSERT(sz > 0 && sz % 2 == 0); + + for(i = 0; i < sz; i += 2) { + int dim = data[i]; + int tag = data[i+1]; + size_t j; + if(dim == 3) { + int* face_dimTags = NULL; + size_t face_dimTags_n, count; + gmshModelMeshSetOutwardOrientation(tag, &ierr); + ERR(gmsh_err_to_res_T(ierr)); + /* Get 2d components of the 3d object */ + gmshModelGetBoundary(data+i, 2, &face_dimTags, &face_dimTags_n, 1, 0, 0, &ierr); + dimTags_to_free = face_dimTags; + ERR(gmsh_err_to_res_T(ierr)); + ASSERT(face_dimTags_n % 2 == 0); + /* copy tags in the output */ + count = darray_int_size_get(tags); + ERR(darray_int_reserve(tags, count + face_dimTags_n/2)); + for(j = 0; j < face_dimTags_n; j += 2) { + ASSERT(face_dimTags[j] == 2); /* dim == 2 */ + ERR(darray_int_push_back(tags, &face_dimTags[j+1])); + } + gmshFree(dimTags_to_free); dimTags_to_free = NULL; + } + else if(dim == 2) { + ERR(darray_int_push_back(tags, &data[i+1])); + } else { + res = RES_BAD_ARG; + goto error; + } + } +exit: + gmshFree(dimTags_to_free); + return res; +error: + goto exit; } /****************************************************************************** @@ -261,241 +302,255 @@ error: } res_T -scad_stl_export +scad_stl_get_data (struct scad_geometry* geometry, - const char* prefix, - const int reverse, - const int binary) + struct darray_double* triangles) { - 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; + return scad_stl_get_data_partial(geometry, NULL, 0, triangles); +} + +res_T +scad_stl_get_data_partial + (struct scad_geometry* geometry, + struct scad_geometry** dont, + const size_t dont_count, + struct darray_double* triangles) +{ + size_t* nodeTags = NULL; + double* coord = NULL; + double* pCoord = NULL; + size_t i, tcount, sz, dsz = 0; int ierr = 0; - int* data; - size_t sz; - int str_initialized = 0; + int *data, *ddata = NULL; struct scad_device* dev = get_device(); struct mem_allocator* allocator = NULL; + struct darray_int tags; + struct darray_int dtags; + int tags_initialized = 0; res_T res = RES_OK; - if(!geometry) { + if(!geometry || !triangles) { res = RES_BAD_ARG; goto error; } ERR(check_device(FUNC_NAME)); - sz = geometry->gmsh_dimTags_n; - data = geometry->gmsh_dimTags; - ASSERT(sz > 0 && sz % 2 == 0); - 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; + darray_int_init(allocator, &tags); + darray_int_init(allocator, &dtags); + tags_initialized = 1; + + /* Get 2d tags */ + ERR(get_2d_tags(geometry, &tags)); + sz = darray_int_size_get(&tags); + data = darray_int_data_get(&tags); + + if(dont) { + for(i = 0; i < dont_count; i++) { + ERR(get_2d_tags(dont[i], &dtags)); } - ERR(str_copy(&filename, &geometry->name)); + dsz = darray_int_size_get(&dtags); + ddata = darray_int_data_get(&dtags); + /* Sort ddata tags to enable bsearch */ + qsort(ddata, dsz, sizeof(*ddata), int_compare); } - 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)); + /* Collect triangles */ + tcount = 0; + for(i = 0; i < sz; i++) { + size_t count, j, coord_n, pCoord_n, nodeTags_n; + const int type = 2; /* 3-node triangles (see src/common/GmshDefines.h) */ + if(bsearch(data+i, ddata, dsz, sizeof(*ddata), int_compare)) { + /* this tag is part of the dont list: don't collect */ + continue; } - 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; + gmshModelMeshGetNodesByElementType(type, &nodeTags, &nodeTags_n, + &coord, &coord_n, &pCoord, &pCoord_n, data[i], 0, &ierr); 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 */ + ASSERT(nodeTags_n % 3 == 0); + ASSERT(coord_n == nodeTags_n * 3); + tcount += coord_n; + count = darray_double_size_get(triangles); + ERR(darray_double_reserve(triangles, count + coord_n)); + for(j = 0; j < coord_n; j++) { + ERR(darray_double_push_back(triangles, &coord[j])); } - if (reverse) gmshModelMeshReverse(data, sz, &ierr); - ERR(gmsh_err_to_res_T(ierr)); - } - - /* 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; - } - - 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]; + gmshFree(nodeTags); nodeTags = NULL; + gmshFree(coord); coord = NULL; + gmshFree(pCoord); pCoord = NULL; } + ASSERT(tcount == darray_double_size_get(triangles)); tcount /= 9; - ASSERT(tcount <= UINT_MAX); - - ERR(write_stl(str_cget(&filename), (unsigned)tcount, coord, coord_n, binary)); exit: 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); + if(tags_initialized) { + darray_int_release(&tags); + darray_int_release(&dtags); } - 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); + gmshFree(nodeTags); + gmshFree(coord); + gmshFree(pCoord); return res; error: if(dev) { - log_error(dev, "%s: could not export to STL -- %s\n", + log_error(dev, "%s: could not get STL data -- %s\n", FUNC_NAME, res_to_cstr(res)); } goto exit; } res_T -scad_stl_export_split +scad_stl_data_write + (struct darray_double* triangles, + const char* filename, + const int binary) +{ + res_T res = RES_OK; + size_t coord_n; + double* coord; + + if(!triangles || !filename) { + res = RES_BAD_ARG; + goto error; + } + + coord_n = darray_double_size_get(triangles); + if(coord_n % 9) { + res = RES_BAD_ARG; + goto error; + } + coord = darray_double_data_get(triangles); + + if(binary) ERR( write_binary_stl(filename, coord, coord_n)); + else ERR(write_ascii_stl(filename, coord, coord_n)); + +exit: + return res; +error: + goto exit; +} + +res_T +scad_stl_export (struct scad_geometry* geometry, const char* prefix, - const int binary) /* FIXME: unused as we use gmshWrite */ + const int binary) { - 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; + struct darray_double trg; + struct scad_device* dev = get_device(); + struct str filename; + int initialized = 0; - (void)binary; + if(!geometry || (!prefix && str_is_empty(&geometry->name))) { + res= RES_BAD_ARG; + goto error; + } + + ERR(check_device(FUNC_NAME)); + + darray_double_init(dev->allocator, &trg); + str_init(dev->allocator, &filename); + initialized = 1; + if(prefix) { + ERR(str_set(&filename, prefix)); + } else { + ERR(str_copy(&filename, &geometry->name)); + } + ERR(str_append(&filename, ".stl")); + ERR(scad_stl_get_data(geometry, &trg)); + ERR(scad_stl_data_write(&trg, str_cget(&filename), binary)); + +exit: + if(initialized) { + darray_double_release(&trg); + str_release(&filename); + } + return res; +error: + goto exit; +} + +res_T +scad_stl_export_partial + (struct scad_geometry* geometry, + struct scad_geometry** dont, + const size_t dont_count, + const char* prefix, + const int binary) +{ + res_T res = RES_OK; + struct darray_double trg; + struct scad_device* dev = get_device(); + struct str filename; + int initialized = 0; if(!geometry) { - res = RES_BAD_ARG; + res= RES_BAD_ARG; goto error; } ERR(check_device(FUNC_NAME)); - sz = geometry->gmsh_dimTags_n; - data = geometry->gmsh_dimTags; - ASSERT(sz % 2 == 0); - - str_init(get_device()->allocator, &filename_root); - str_init(get_device()->allocator, &filename); - str_initialized = 1; + darray_double_init(dev->allocator, &trg); + str_init(dev->allocator, &filename); + initialized = 1; if(prefix) { - ERR(str_set(&filename_root, prefix)); + ERR(str_set(&filename, prefix)); } else { if(str_len(&geometry->name) == 0) { res = RES_BAD_ARG; goto error; } - ERR(str_copy(&filename_root, &geometry->name)); + ERR(str_copy(&filename, &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)); + ERR(str_append(&filename, ".stl")); + ERR(scad_stl_get_data_partial(geometry, dont, dont_count, &trg)); + ERR(scad_stl_data_write(&trg, str_cget(&filename), binary)); - 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: + if(initialized) { + darray_double_release(&trg); + str_release(&filename); + } + return res; +error: + goto exit; +} - gmshModelGetBoundary(data, sz, &tagout, &tagoutn, 1, 0, 0, &ierr); - ERR(gmsh_err_to_res_T(ierr)); +res_T +scad_stl_export_split + (struct scad_geometry* geometry, + const char* prefix, + const int binary) +{ + size_t i; + struct scad_geometry** parts = NULL; + struct scad_device* dev = get_device(); + size_t count; + res_T res = RES_OK; + (void)binary; - 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(!geometry || (!prefix && str_is_empty(&geometry->name))) { + res = RES_BAD_ARG; + goto error; + } - dimtag[0]=2; - dimtag[1]=group; - gmshModelRemovePhysicalGroups(dimtag, 2, &ierr); - ERR(gmsh_err_to_res_T(ierr)); - } + ERR(check_device(FUNC_NAME)); + + ERR(scad_geometry_explode(geometry, prefix, &parts, &count)); + ASSERT(count*2 == geometry->gmsh_dimTags_n); + for(i = 0; i < count; i++) { + ERR(scad_stl_export(parts[i], NULL, binary)); } exit: - if(str_initialized) { - str_release(&filename_root); - str_release(&filename); - } - gmshFree(tagout); + MEM_RM(dev->allocator, parts); return res; error: goto exit; diff --git a/src/scad.h b/src/scad.h @@ -40,6 +40,7 @@ struct mem_allocator; struct logger; struct str; +struct darray_double; /* Forward declaration of scad opaque data types */ struct scad_geometry; /* Wrapping of dimTags gmsh description */ @@ -158,7 +159,11 @@ scad_synchronize ******************************************************************************/ SCAD_API res_T -scad_geometry_delete +scad_geometry_ref_get + (struct scad_geometry* geom); + +SCAD_API res_T +scad_geometry_ref_put (struct scad_geometry* geom); SCAD_API res_T @@ -353,6 +358,11 @@ scad_geometry_rename (struct scad_geometry* geom, const char* name); /* Can be NULL */ +/* Reverse the orientation of the geomery `geome' */ +SCAD_API res_T +scad_geometry_reverse + (struct scad_geometry* geom); + /* Translate the geometry `geom' along (`dx', `dy', `dz'). */ SCAD_API res_T scad_geometry_translate @@ -368,7 +378,7 @@ scad_geometry_rotate const double dir[3], const double angle); -/* Extrude the geometry `geom' using a translation along (`dx', `dy', `dz').*/ +/* Extrude the geometry `geom' using a translation along (`dx', `dy', `dz'). */ SCAD_API res_T scad_geometry_extrude (const struct scad_geometry* geom, @@ -376,26 +386,29 @@ scad_geometry_extrude const double dxdydz[3], struct scad_geometry** out_geometry); -/* Return a list of geometries which form the geometry geom */ +/* Return a list of geometries which form the geometry `geom'. + * Note that `out_geometries' is allocated using the allocator provided when + * initializing star-cad and should be freed accordingly. */ SCAD_API res_T scad_geometry_explode (const struct scad_geometry* geom, const char* prefix_name, /* Can be NULL */ - struct scad_geometry*** out_geometry, + struct scad_geometry*** out_geometries, 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 +/* Import a step model from file `filename'. The imported geometries are + * recorded in `out_geometries'. + * Note that `out_geometries' 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, + struct scad_geometry*** out_geometries, size_t* out_geometry_n); /* Export the geometry `geom' to an STL file. + * In order to get a mesh, one has to call scad_scene_mesh before calling this. * 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 @@ -404,15 +417,47 @@ 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 */ +/* Same as previous, but geometries that are part of `exclude' is not exported */ +SCAD_API res_T +scad_stl_export_partial + (struct scad_geometry* geometry, + struct scad_geometry** exclude, + const size_t exclude_count, + const char* prefix, + const int binary); + +/* Export the geometry `geom' in as many files than its count. */ 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 */ +/* Get the mesh of the geometry `geom' into a darray_double, each triangle + * described * by 9 doubles in a STL way. + * In order to get a mesh, one has to call scad_scene_mesh before calling this. */ +SCAD_API res_T +scad_stl_get_data + (struct scad_geometry* geom, + struct darray_double* triangles); /* Can contain some triangles already */ + +/* Same as previous, but geometries that are part of `exclude' is not collected */ +SCAD_API res_T +scad_stl_get_data_partial + (struct scad_geometry* geometry, + struct scad_geometry** exclude, + const size_t exclude_count, + struct darray_double* triangles); /* Can contain some triangles already */ + +/* Write the mesh the same way stl_export do, using data as returned by + * stl_get_data[_partial] */ +SCAD_API res_T +scad_stl_data_write + (struct darray_double* triangles, + const char* filename, + const int binary); SCAD_API res_T scad_scene_write diff --git a/src/scad_c.h b/src/scad_c.h @@ -18,6 +18,9 @@ #include <rsys/rsys.h> +struct scad_geometry; +struct darray_int; + #define ERR(Expr) if((res = (Expr)) != RES_OK) goto error; else (void)0 static INLINE res_T @@ -33,4 +36,9 @@ gmsh_err_to_res_T(const int ierr) return res; } +res_T +get_2d_tags + (struct scad_geometry* geometry, + struct darray_int* tags); + #endif diff --git a/src/scad_device.c b/src/scad_device.c @@ -45,6 +45,8 @@ device_release(struct scad_device* dev) 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); + dev->log = log; + dev->log_type = log_type; /* Duplicate the htable we iterate on as dev->allgeom will be altered during * the process (through calls to geometry_release) */ @@ -57,7 +59,7 @@ device_release(struct scad_device* dev) } 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)); + SCAD(geometry_ref_put(geom)); htable_geometries_iterator_next(&it); } htable_names_release(&dev->geometry_names); @@ -315,6 +317,9 @@ scad_initialize g_device->allocator = allocator; g_device->need_synchro = g_device->options.Misc.DebugOpenCascadeSync; g_device->verbose = verbose; + g_device->log_type = LOG_OUTPUT; + g_device->log + = (g_device->options.Misc.LogOpenCascadeTagsRefCounting== Scad_log_all); htable_names_init(allocator, &g_device->geometry_names); htable_geometries_init(allocator, &g_device->allgeom); htable_tags2geom_init(allocator, &g_device->tags2geom[0]); diff --git a/src/scad_device.h b/src/scad_device.h @@ -73,6 +73,9 @@ struct scad_device { int need_synchro; ref_T ref; + /* Used only when releasing the geometry */ + int log; + enum log_type log_type; }; /******************************************************************************* diff --git a/src/scad_geometry.c b/src/scad_geometry.c @@ -43,6 +43,8 @@ #define HTABLE_DATA size_t #include <rsys/hash_table.h> +#include <limits.h> + /******************************************************************************* * Utility functions ******************************************************************************/ @@ -105,6 +107,32 @@ error: goto exit; } +static int +mixed_dim_err_msg + (const char* name, /* Can be NULL */ + const char* op, + struct scad_geometry** geometries, + const size_t geometries_count, + int* dim) +{ + size_t i, j; + struct scad_device* dev = get_device(); + ASSERT(op && (geometries || geometries_count == 0) && dim); + for(i = 0; i < geometries_count; i++) { + for(j = 0; j < geometries[i]->gmsh_dimTags_n; j += 2) { + int d = geometries[i]->gmsh_dimTags[j]; + if(*dim == INT_MAX) *dim = d; + else if (*dim != d) { + log_error(dev, + "Dimension mismatch in %s operation creating '%s' geometry.\n", + op, (name ? name : "unamed")); + return 1; + } + } + } + return 0; +} + static res_T scad_geometry_create (const char* name, @@ -115,12 +143,8 @@ scad_geometry_create 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) { @@ -128,6 +152,7 @@ scad_geometry_create goto error; } + ref_init(&geom->ref); str_init(allocator, &geom->name); ERR(htable_geometries_set(&dev->allgeom, &geom, &one)); ERR(geom_set_name(geom, name)); @@ -138,7 +163,7 @@ end: return res; error: if(geom) { - CHK(RES_OK == geometry_release(log, LOG_OUTPUT, geom)); + SCAD(geometry_ref_put(geom)); geom = NULL; } goto end; @@ -159,7 +184,7 @@ gather_tags struct htable_tags t2, t3; struct htable_tags_iterator it, end; - ASSERT((geometries || !geometries_count) && (out_dimTags || !out_dimTags_n)); + ASSERT((geometries || !geometries_count) && out_dimTags && out_dimTags_n); htable_tags_init(allocator, &t2); htable_tags_init(allocator, &t3); @@ -236,22 +261,18 @@ free_gmsh_map /******************************************************************************* * Local functions ******************************************************************************/ -res_T -geometry_release - (const int log, - const enum log_type log_type, - struct scad_geometry* geom) +static void +geometry_release(ref_T* ref) { + 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); + ASSERT(ref); dev->need_synchro = 1; - - ERR(device_unregister_tags(log, log_type, geom)); + geom = CONTAINER_OF(ref, struct scad_geometry, ref); + CHK(RES_OK == device_unregister_tags(dev->log, dev->log_type, geom)); MEM_RM(allocator, geom->gmsh_dimTags); if(str_len(&geom->name) != 0) { n = htable_names_erase(&dev->geometry_names, &geom->name); @@ -260,41 +281,32 @@ geometry_release str_release(&geom->name); n = htable_geometries_erase(&dev->allgeom, &geom); ASSERT(n == 1); (void)n; +#ifndef NDEBUG + geom->gmsh_dimTags = (int*)0xdeadbeef; +#endif MEM_RM(allocator, geom); - -end: - return res; -error: - goto end; } /****************************************************************************** * Exported functions *****************************************************************************/ res_T -scad_geometry_delete +scad_geometry_ref_get (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)); + if(!geom) return RES_BAD_ARG; + ref_get(&geom->ref); + return RES_OK; +} -end: - return res; -error: - goto end; +res_T +scad_geometry_ref_put + (struct scad_geometry* geom) +{ + if(!geom) return RES_BAD_ARG; + ref_put(&geom->ref, geometry_release); + CHK(RES_OK == check_device(FUNC_NAME)); + return RES_OK; } res_T @@ -322,20 +334,24 @@ scad_scene_clear 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"); + SWAP(int, dev->log, log); + SWAP(enum log_type, dev->log_type, log_type); + if(dev->log) { + logger_print(dev->logger, dev->log_type, "Clearing scene.\n"); if(empty) { - logger_print(dev->logger, log_type, "scene is empty.\n"); + logger_print(dev->logger, dev->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)); + ERR(scad_geometry_ref_put(geom)); htable_geometries_iterator_next(&it); } - if(log) { - logger_print(dev->logger, log_type, "End clearing scene.\n"); + if(dev->log) { + logger_print(dev->logger, dev->log_type, "End clearing scene.\n"); } + SWAP(int, log, dev->log); + SWAP(enum log_type, log_type, dev->log_type); /* Ensure clear is complete (not scad-registered tags) */ gmshClear(&ierr); @@ -518,8 +534,6 @@ scad_add_rectangle 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; @@ -528,8 +542,6 @@ scad_add_rectangle 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)); @@ -552,7 +564,7 @@ exit: return res; error: if(geom) { - CHK(RES_OK == geometry_release(log, LOG_OUTPUT, geom)); + SCAD(geometry_ref_put(geom)); geom = NULL; } goto exit; @@ -570,8 +582,6 @@ scad_add_disk 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; @@ -580,8 +590,6 @@ scad_add_disk 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)); @@ -604,7 +612,7 @@ exit: return res; error: if(geom) { - CHK(RES_OK == geometry_release(log, LOG_OUTPUT, geom)); + SCAD(geometry_ref_put(geom)); geom = NULL; } goto exit; @@ -628,8 +636,6 @@ scad_add_polygon 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; @@ -638,8 +644,6 @@ scad_add_polygon 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)); @@ -688,7 +692,7 @@ exit: return res; error: if(geom) { - CHK(RES_OK == geometry_release(log, LOG_OUTPUT, geom)); + SCAD(geometry_ref_put(geom)); geom = NULL; } goto exit; @@ -706,8 +710,6 @@ scad_add_box 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; @@ -716,8 +718,6 @@ scad_add_box 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)); @@ -739,7 +739,7 @@ exit: return res; error: if(geom) { - CHK(RES_OK == geometry_release(log, LOG_OUTPUT, geom)); + SCAD(geometry_ref_put(geom)); geom = NULL; } goto exit; @@ -759,8 +759,6 @@ scad_add_cylinder 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; @@ -769,8 +767,6 @@ scad_add_cylinder 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); @@ -793,7 +789,7 @@ exit: return res; error: if(geom) { - CHK(RES_OK == geometry_release(log, LOG_OUTPUT, geom)); + SCAD(geometry_ref_put(geom)); geom = NULL; } goto exit; @@ -811,8 +807,6 @@ scad_add_sphere 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; @@ -821,8 +815,6 @@ scad_add_sphere 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); @@ -845,7 +837,7 @@ exit: return res; error: if(geom) { - CHK(RES_OK == geometry_release(log, LOG_OUTPUT, geom)); + SCAD(geometry_ref_put(geom)); geom = NULL; } goto exit; @@ -871,8 +863,6 @@ scad_fuse_geometries 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; @@ -881,8 +871,6 @@ scad_fuse_geometries 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)); @@ -916,7 +904,7 @@ exit: return res; error: if(geom) { - CHK(RES_OK == geometry_release(log, LOG_OUTPUT, geom)); + SCAD(geometry_ref_put(geom)); geom = NULL; } if(tagout) gmshModelOccRemove(tagout, tagoutn, 1, &ierr); @@ -943,8 +931,6 @@ scad_cut_geometries 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; @@ -953,8 +939,6 @@ scad_cut_geometries 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)); @@ -987,9 +971,14 @@ exit: free_gmsh_map(map, mapnn); return res; error: - if(geom) { - CHK(RES_OK == geometry_release(log, LOG_OUTPUT, geom)); - geom = NULL; + if(ierr) { + int dim = INT_MAX; + if(!mixed_dim_err_msg(name, "cut", geometries, geometries_count, &dim)) + mixed_dim_err_msg(name, "cut", tools, tools_count, &dim); + if(geom) { + SCAD(geometry_ref_put(geom)); + geom = NULL; + } } if(tagout) gmshModelOccRemove(tagout, tagoutn, 1, &ierr); goto exit; @@ -1015,8 +1004,6 @@ scad_intersect_geometries 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; @@ -1025,8 +1012,6 @@ scad_intersect_geometries 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)); @@ -1064,7 +1049,7 @@ exit: return res; error: if(geom) { - CHK(RES_OK == geometry_release(log, LOG_OUTPUT, geom)); + SCAD(geometry_ref_put(geom)); geom = NULL; } if(tagout) gmshModelOccRemove(tagout, tagoutn, 1, &ierr); @@ -1094,8 +1079,6 @@ scad_geometries_common_boundaries 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; @@ -1104,8 +1087,6 @@ scad_geometries_common_boundaries 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)); @@ -1124,7 +1105,7 @@ scad_geometries_common_boundaries geom->gmsh_dimTags_n = tagoutn; if (tagoutn == 0) { geom->gmsh_dimTags = NULL; - } else { + } else { geom->gmsh_dimTags = MEM_ALLOC(allocator, tagoutn * sizeof(*tagout)); if(!geom->gmsh_dimTags) { res = RES_MEM_ERR; @@ -1148,8 +1129,17 @@ exit: free_gmsh_map(map, mapnn); return res; error: + if(ierr) { + int dim = INT_MAX; + if(!mixed_dim_err_msg(name, "common boundary", geometries, geometries_count, &dim)) + mixed_dim_err_msg(name, "common boundary", tools, tools_count, &dim); + if(geom) { + SCAD(geometry_ref_put(geom)); + geom = NULL; + } + } if(geom) { - CHK(RES_OK == geometry_release(log, LOG_OUTPUT, geom)); + SCAD(geometry_ref_put(geom)); geom = NULL; } if(tagout) gmshModelOccRemove(tagout, tagoutn, 1, &ierr); @@ -1204,8 +1194,6 @@ scad_geometry_extrude 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; @@ -1214,8 +1202,6 @@ scad_geometry_extrude 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); @@ -1255,7 +1241,7 @@ exit: return res; error: if(extrude_geom) { - CHK(RES_OK == geometry_release(log, LOG_OUTPUT, extrude_geom)); + SCAD(geometry_ref_put(extrude_geom)); extrude_geom = NULL; } goto exit; @@ -1276,6 +1262,7 @@ scad_geometry_explode int name_initialized = 0; struct scad_device* dev = get_device(); struct mem_allocator* allocator = NULL; + const char* base_name = NULL; if(!geom || !out_geometry || !out_geometry_n) { res = RES_BAD_ARG; @@ -1283,8 +1270,8 @@ scad_geometry_explode } ERR(check_device(FUNC_NAME)); - allocator = dev->allocator; + allocator = dev->allocator; data = geom->gmsh_dimTags; sz = geom->gmsh_dimTags_n; @@ -1295,13 +1282,14 @@ scad_geometry_explode goto error; } - if(prefix_name) { + base_name = prefix_name ? prefix_name : str_cget(&geom->name); + if(base_name) { str_init(allocator, &name); name_initialized = 1; } for(i=0; i<sz/2; ++i) { - if(prefix_name) { - ERR(str_set(&name, prefix_name)); + if(base_name) { + ERR(str_set(&name, base_name)); ERR(str_append_printf(&name,"_%lu", (unsigned long)i)); ERR(scad_geometry_create(str_cget(&name), geom_array+i)); } else { @@ -1328,7 +1316,7 @@ exit: error: if(geom_array) { for(i = 0; i < sz/2; i++) { - if(geom_array[i]) SCAD(geometry_delete(geom_array[i])); + if(geom_array[i]) SCAD(geometry_ref_put(geom_array[i])); } MEM_RM(allocator, geom_array); geom_array = NULL; @@ -1351,8 +1339,6 @@ scad_geometry_copy 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; @@ -1361,8 +1347,6 @@ scad_geometry_copy 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; @@ -1387,7 +1371,7 @@ exit: return res; error: if(copy) { - CHK(RES_OK == geometry_release(log, LOG_OUTPUT, copy)); + SCAD(geometry_ref_put(copy)); copy = NULL; } if(tagout) gmshModelOccRemove(tagout, tagoutn, 1, &ierr); @@ -1417,6 +1401,29 @@ error: } res_T +scad_geometry_reverse + (struct scad_geometry* geom) +{ + int ierr = 0; + res_T res = RES_OK; + + if(!geom) { + res = RES_BAD_ARG; + goto error; + } + + ERR(check_device(FUNC_NAME)); + + gmshModelMeshReverse(geom->gmsh_dimTags, geom->gmsh_dimTags_n, &ierr); + 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]) @@ -1468,8 +1475,6 @@ scad_geometries_partition 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; @@ -1478,13 +1483,11 @@ scad_geometries_partition 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 + * only removed from scad_geometry_ref_put 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. */ @@ -1619,7 +1622,9 @@ exit: error: if(geoms) { for(i = 0; i < geometries_count; i++) { - if(geoms[i]) CHK(RES_OK == geometry_release(log, LOG_OUTPUT, geoms[i])); + if(geoms[i]) { + SCAD(geometry_ref_put(geoms[i])); + } } } if(tagout) { @@ -1648,8 +1653,6 @@ scad_fragment_geometries 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; @@ -1658,8 +1661,6 @@ scad_fragment_geometries 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)); @@ -1693,15 +1694,13 @@ exit: return res; error: if(geom) { - CHK(RES_OK == geometry_release(log, LOG_OUTPUT, geom)); + SCAD(geometry_ref_put(geom)); geom = NULL; } if(tagout) gmshModelOccRemove(tagout, tagoutn, 1, &ierr); goto exit; } - - res_T scad_geometry_boundary (const char* name, @@ -1717,8 +1716,6 @@ scad_geometry_boundary 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; @@ -1727,8 +1724,6 @@ scad_geometry_boundary 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); @@ -1752,7 +1747,7 @@ exit: return res; error: if(geom) { - CHK(RES_OK == geometry_release(log, LOG_OUTPUT, geom)); + SCAD(geometry_ref_put(geom)); geom = NULL; } if(tagout) gmshModelOccRemove(tagout, tagoutn, 1, &ierr); @@ -1831,7 +1826,7 @@ error: ga_sz = 0; if(geom_array) { for(i=0; i<ga_sz; ++i) { - if(geom_array[i]) SCAD(geometry_delete(geom_array[i])); + if(geom_array[i]) SCAD(geometry_ref_put(geom_array[i])); } MEM_RM(allocator, geom_array); geom_array = NULL; @@ -1857,6 +1852,9 @@ scad_geometry_normal struct scad_geometry* out = NULL; struct scad_device* dev = get_device(); struct mem_allocator* allocator = NULL; + double* coord = NULL; + double* pcoord = NULL; + double* normals = NULL; if(!geom || !p || !N || !out_geometry) { res = RES_BAD_ARG; @@ -1879,11 +1877,8 @@ scad_geometry_normal 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]; @@ -1910,17 +1905,21 @@ scad_geometry_normal ERR(device_register_tags(out)); d3_set(N, normals); - gmshFree(coord); - gmshFree(pcoord); - gmshFree(normals); break; } } + if(!out) { /* Could not find a matching surface */ + res = RES_BAD_ARG; + goto error; + } exit: + gmshFree(coord); + gmshFree(pcoord); + gmshFree(normals); if(out_geometry) *out_geometry = out; if(allocator) MEM_RM(allocator, data); - if(surface) scad_geometry_delete(surface); + if(surface) SCAD(geometry_ref_put(surface)); return res; error: goto exit; diff --git a/src/scad_geometry.h b/src/scad_geometry.h @@ -21,17 +21,16 @@ #include <rsys/rsys.h> #include <rsys/str.h> #include <rsys/logger.h> +#include <rsys/ref_count.h> + +struct mem_allocator; 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); + ref_T ref; +}; #endif diff --git a/src/test_api.c b/src/test_api.c @@ -22,6 +22,7 @@ #include <rsys/math.h> #include <rsys/mem_allocator.h> #include <rsys/logger.h> +#include <rsys/dynamic_array_double.h> #include <stdlib.h> #include <stdio.h> @@ -54,6 +55,7 @@ main(int argc, char* argv[]) struct scad_geometry* out_geoms[2]; struct mem_allocator allocator; struct logger logger; + struct darray_double trg; const char* name; size_t i, c; @@ -107,8 +109,11 @@ main(int argc, char* argv[]) geoms[1] = geom2; OK(scad_add_sphere(NULL, p1, .1, &geom)); - BAD(scad_geometry_delete(NULL)); - OK(scad_geometry_delete(geom)); + BAD(scad_geometry_ref_get(NULL)); + OK(scad_geometry_ref_get(geom)); + BAD(scad_geometry_ref_put(NULL)); + OK(scad_geometry_ref_put(geom)); + OK(scad_geometry_ref_put(geom)); BAD(scad_geometry_get_count(NULL, &c)); BAD(scad_geometry_get_count(geom1, NULL)); @@ -119,13 +124,13 @@ main(int argc, char* argv[]) 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)); + OK(scad_geometry_ref_put(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)); + OK(scad_geometry_ref_put(geom)); BAD(scad_add_polygon(NULL, NULL, coord, 0, 3, &poly)); BAD(scad_add_polygon(NULL, get_position, coord, 0, 2, &poly)); @@ -137,7 +142,7 @@ main(int argc, char* argv[]) 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)); + OK(scad_geometry_ref_put(geom)); BAD(scad_add_cylinder(NULL, NULL, d1, 2, 1, &geom)); BAD(scad_add_cylinder(NULL, p1, NULL, 2, 1, &geom)); @@ -146,13 +151,13 @@ main(int argc, char* argv[]) 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)); + OK(scad_geometry_ref_put(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)); + OK(scad_geometry_ref_put(geom)); /* BAD(scad_fuse_geometries(NULL, NULL, 0, geoms, 2, &geom)); */ BAD(scad_fuse_geometries(NULL, geoms, 2, NULL, 0, &geom)); @@ -160,7 +165,7 @@ main(int argc, char* argv[]) 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)); + OK(scad_geometry_ref_put(geom)); /* BAD(scad_cut_geometries(NULL, NULL, 0, geoms, 2, &geom)); */ BAD(scad_cut_geometries(NULL, geoms, 2, NULL, 0, &geom)); @@ -168,7 +173,7 @@ main(int argc, char* argv[]) 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)); + OK(scad_geometry_ref_put(geom)); /* BAD(scad_intersect_geometries(NULL, NULL, 0, geoms, 2, &geom)); */ BAD(scad_intersect_geometries(NULL, geoms, 2, NULL, 0, &geom)); @@ -176,7 +181,7 @@ main(int argc, char* argv[]) 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)); + OK(scad_geometry_ref_put(geom)); /* BAD(scad_geometries_common_boundaries(NULL, NULL, 0, geoms, 2, &geom)); */ BAD(scad_geometries_common_boundaries(NULL, geoms, 2, NULL, 0, &geom)); @@ -184,25 +189,25 @@ main(int argc, char* argv[]) 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)); + OK(scad_geometry_ref_put(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])); + OK(scad_geometry_ref_put(out_geoms[0])); + OK(scad_geometry_ref_put(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)); + OK(scad_geometry_ref_put(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)); + OK(scad_geometry_ref_put(geom)); BAD(scad_geometry_rename(NULL, NULL)); BAD(scad_geometry_rename(geom2, "sphere 1")); /* Name already in use */ @@ -221,33 +226,52 @@ main(int argc, char* argv[]) 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)); + OK(scad_geometry_ref_put(poly)); + OK(scad_geometry_ref_put(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])); + OK(scad_geometry_ref_put(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)); + darray_double_init(&allocator, &trg); + BAD(scad_stl_get_data(NULL, NULL)); + BAD(scad_stl_get_data(geom2, NULL)); + OK(scad_stl_get_data(geom2, &trg)); + BAD(scad_stl_data_write(NULL, NULL, 0)); + BAD(scad_stl_data_write(&trg, NULL, 0)); + BAD(scad_stl_data_write(NULL, "/tmp/test", 0)); + OK(scad_stl_data_write(&trg, "/tmp/test", 0)); + OK(scad_stl_data_write(&trg, "/tmp/test", 1)); + darray_double_release(&trg); + + BAD(scad_stl_export(NULL, NULL, 0)); + BAD(scad_stl_export(geom2, NULL, 0)); /* geom2 has no name */ + OK(scad_stl_export(geom2, "/tmp/test", 0)); + OK(scad_stl_export(geom2, "/tmp/test", 1)); + OK(scad_stl_export(geom1, NULL, 0)); + OK(scad_stl_export(geom1, NULL, 1)); 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)); + BAD(scad_stl_export_split(geom2, "/tmp/test", 0)); /* Produce same names */ + OK(scad_stl_export_split(geom2, "/tmp/testb", 1)); OK(scad_stl_export_split(geom1, NULL, 0)); + BAD(scad_stl_export_split(geom1, NULL, 1)); /* Produce same names */ + OK(scad_stl_export_split(geom1, "different_names", 1)); + + BAD(scad_geometry_reverse(NULL)); + OK(scad_geometry_reverse(geom1)); BAD(scad_geometry_get_name(NULL, &name)); BAD(scad_geometry_get_name(geom1, NULL)); diff --git a/src/test_export.c b/src/test_export.c @@ -54,14 +54,14 @@ main(int argc, char* argv[]) 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(rectangle, NULL, 0)); + OK(scad_stl_export(rectangle, "bin_rectangle", 1)); - OK(scad_stl_export(cube, NULL, 0, 0)); - OK(scad_stl_export(cube, "bin_cube", 0, 1)); + OK(scad_stl_export(cube, NULL, 0)); + OK(scad_stl_export(cube, "bin_cube", 1)); - OK(scad_stl_export(cube2, NULL, 0, 0)); - OK(scad_stl_export(cube2, "bin_cube2", 0, 1)); + OK(scad_stl_export(cube2, NULL, 0)); + OK(scad_stl_export(cube2, "bin_cube2", 1)); OK(scad_finalize()); diff --git a/src/test_partition.c b/src/test_partition.c @@ -65,14 +65,14 @@ two_geoms if(allow_overlapping) { snprintf(name, sizeof(name), "part_%g_1o", x); - OK(scad_stl_export(out_geoms[0], name, 0, 0)); + OK(scad_stl_export(out_geoms[0], name, 0)); snprintf(name, sizeof(name), "part_%g_2o", x); - OK(scad_stl_export(out_geoms[1], name, 0, 1)); + OK(scad_stl_export(out_geoms[1], name, 1)); } else { snprintf(name, sizeof(name), "part_%g_1", x); - OK(scad_stl_export(geoms[0], name, 0, 0)); + OK(scad_stl_export(geoms[0], name, 0)); snprintf(name, sizeof(name), "part_%g_2", x); - OK(scad_stl_export(geoms[1], name, 0, 1)); + OK(scad_stl_export(geoms[1], name, 1)); } end: