commit 7dc0f8b4dfcabf20183834e53fac1e4a219c2ea8
parent acb875d4315820ef0da2f1daf90022a30dd914d6
Author: Christophe Coustet <christophe.coustet@meso-star.com>
Date: Fri, 24 Nov 2023 17:00:57 +0100
Merge branch 'release_0.3'
Diffstat:
12 files changed, 1980 insertions(+), 628 deletions(-)
diff --git a/README.md b/README.md
@@ -21,7 +21,20 @@ project from the `cmake/CMakeLists.txt` file by appending to the
## Release notes
-### Version 0.1.1
+### Version 0.3
+
+This release largely breaks the API.
+
+ - Add API calls to attach custom data to geometries
+ - Increase default min size for generated meshes
+ - Add API calls to deals with the entities hidden in geometries
+ - Change geometry swap API
+ - Remove redundant API calls
+ - BugFixes on geometries lifetime management
+ - Log messages improvement
+ - Many bug fixes
+
+### Version 0.2
- Introduce reference counting for geometries (API break).
- Add the scad_stl_data_write API call to write a vector of coordinate as an STL
diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt
@@ -26,18 +26,24 @@ option(NO_TEST "Disable the test" OFF)
find_package(gmsh 4.9.5 REQUIRED)
find_package(RCMake 0.4.1 REQUIRED)
find_package(RSys 0.12.1 REQUIRED)
+find_package(StarGeom3D 0.1 REQUIRED)
+find_package(StarEnc3D 0.5 REQUIRED)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${RCMAKE_SOURCE_DIR})
include(rcmake)
include(rcmake_runtime)
-include_directories(${GMSH_INCLUDE_DIR} ${RSys_INCLUDE_DIR})
+include_directories(
+ ${GMSH_INCLUDE_DIR}
+ ${RSys_INCLUDE_DIR}
+ ${StarGeom3D_INCLUDE_DIR}
+ ${StarEnc3D_INCLUDE_DIR})
################################################################################
# Configure and define targets
################################################################################
set(VERSION_MAJOR 0)
-set(VERSION_MINOR 2)
+set(VERSION_MINOR 3)
set(VERSION_PATCH 0)
set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH})
@@ -58,7 +64,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_LIBRARY} m)
+target_link_libraries(scad RSys ${GMSH_LIBRARY} m StarGeom3D StarEnc3D)
set_target_properties(scad PROPERTIES
DEFINE_SYMBOL SCAD_SHARED_BUILD
@@ -94,6 +100,7 @@ if(NOT NO_TEST)
new_test(test_api)
new_test(test_export)
new_test(test_partition)
+ new_test(test_lifetime)
rcmake_copy_runtime_libraries(test)
diff --git a/src/scad.c b/src/scad.c
@@ -28,7 +28,9 @@
#include <rsys/double3.h>
#include <rsys/float3.h>
-#include <star/sstl.h>
+#include <star/sg3d.h>
+#include <star/sg3d_sencXd_helper.h>
+#include <star/senc3d.h>
#include <stdio.h>
#include <stdlib.h>
@@ -46,14 +48,15 @@ int_compare(const void *a_, const void *b_) {
static res_T
write_ascii_stl
(const char* filename,
- double* coord,
- const size_t coord_n)
+ double* coords,
+ const size_t count)
{
res_T res = RES_OK;
size_t i;
+ size_t actual_trg_count = 0;
FILE* stl_file = NULL;
- ASSERT(filename && (coord || coord_n == 0) && (coord_n % 9 == 0));
+ ASSERT(filename && (coords || count == 0));
stl_file = fopen(filename, "w");
if(!stl_file) {
@@ -63,23 +66,17 @@ write_ascii_stl
OKP(fprintf(stl_file, "solid %s\n", filename));
- /* trg_count triangles split in coord_n blocs */
- for(i = 0; i < coord_n; i += 9) {
+ for(i = 0; i < count; i++) {
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);
+ double vtx[3][3], tmp[3], edge1[3], edge2[3], n[3] = { 0,0,0 };
+ const double zero[3] = { 0,0,0 };
+ d3_set(vtx[0], coords+9*i+0);
+ d3_set(vtx[1], coords+9*i+3);
+ d3_set(vtx[2], coords+9*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;
- }
+ if(d3_eq(tmp, zero)) continue;
d3_normalize(n, tmp);
OKP(fprintf(stl_file, " facet normal %g %g %g\n", SPLIT3(n)));
@@ -89,8 +86,16 @@ write_ascii_stl
}
OKP(fprintf(stl_file, " endloop\n"));
OKP(fprintf(stl_file, " endfacet\n"));
+ actual_trg_count++;
}
OKP(fprintf(stl_file, "endsolid \n"));
+ if(actual_trg_count != count) {
+ long long unsigned del_count = count - actual_trg_count;
+ ASSERT(actual_trg_count < count);
+ log_warning(get_device(),
+ "In file '%s': %llu nul triangle(s) removed in exported geometry.\n",
+ filename, del_count);
+ }
exit:
if(stl_file) fclose(stl_file);
@@ -104,19 +109,20 @@ error:
static res_T
write_binary_stl
(const char* filename,
- double* coord,
- const size_t coord_n)
+ double* coords,
+ const size_t count)
{
res_T res = RES_OK;
- size_t i;
- unsigned trg_count;
+ unsigned i;
+ unsigned trg_count, actual_trg_count = 0;
char header[80] = "Binary STL";
FILE* stl_file = NULL;
+ long int offset;
- ASSERT(filename && (coord || coord_n == 0) && (coord_n % 9 == 0));
+ ASSERT(filename && (coords || count == 0));
stl_file = fopen(filename, "wb");
- if(!stl_file) {
+ if(!stl_file || count > UINT_MAX) {
res = RES_IO_ERR;
goto error;
}
@@ -126,32 +132,49 @@ write_binary_stl
goto error;
}
- trg_count = (unsigned)coord_n / 9;
+ trg_count = (unsigned)count;
+ offset = ftell(stl_file);
if(1 != fwrite(&trg_count, 4, 1, stl_file)) {
res = RES_IO_ERR;
goto error;
}
- /* trg_count triangles split in coord_n blocs */
- for(i = 0; i < coord_n; i += 9) {
+ for(i = 0; i < trg_count; i++) {
+ float tmp[3], edge1[3], edge2[3], zero[3] = { 0,0,0 };
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_set_d3(trg.vrtx[0], coords+9*i+0);
+ f3_set_d3(trg.vrtx[1], coords+9*i+3);
+ f3_set_d3(trg.vrtx[2], coords+9*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);
+ if(f3_eq(tmp, zero)) continue;
f3_normalize(trg.n, tmp);
trg.attrib = 0;
if(1 != fwrite(&trg, 50, 1, stl_file)) {
res = RES_IO_ERR;
goto error;
}
+ actual_trg_count++;
+ }
+ if(actual_trg_count != trg_count) {
+ ASSERT(actual_trg_count < trg_count);
+ log_warning(get_device(),
+ "In file '%s': %u nul triangle(s) removed in exported geometry.\n",
+ filename, trg_count - actual_trg_count);
+ /* Need to change count */
+ if(0 != fseek(stl_file, offset, SEEK_SET)) {
+ res = RES_IO_ERR;
+ goto error;
+ }
+ if(1 != fwrite(&actual_trg_count, 4, 1, stl_file)) {
+ res = RES_IO_ERR;
+ goto error;
+ }
}
exit:
@@ -189,8 +212,6 @@ get_2d_tags
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;
@@ -206,7 +227,7 @@ get_2d_tags
gmshFree(dimTags_to_free); dimTags_to_free = NULL;
}
else if(dim == 2) {
- ERR(darray_int_push_back(tags, &data[i+1]));
+ ERR(darray_int_push_back(tags, &tag));
} else {
res = RES_BAD_ARG;
goto error;
@@ -280,6 +301,18 @@ error:
goto exit;
}
+size_t
+scad_get_dimtag_refcount
+ (const int dim,
+ const int tag)
+{
+ struct tag_desc* desc = device_get_description(dim, tag);
+ if(!desc) return SIZE_MAX;
+
+ return desc->refcount;
+}
+
+
res_T
scad_scene_write
(const char* name)
@@ -319,7 +352,7 @@ scad_stl_get_data_partial
size_t* nodeTags = NULL;
double* coord = NULL;
double* pCoord = NULL;
- size_t i, tcount, sz, dsz = 0;
+ size_t i, count0, tcount, sz, dsz = 0;
int ierr = 0;
int *data, *ddata = NULL;
struct scad_device* dev = get_device();
@@ -357,31 +390,41 @@ scad_stl_get_data_partial
}
/* Collect triangles */
- tcount = 0;
+ count0 = tcount = darray_double_size_get(triangles);
for(i = 0; i < sz; i++) {
- size_t count, j, coord_n, pCoord_n, nodeTags_n;
+ size_t 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)) {
+ const int tag = data[i];
+ if(bsearch(&tag, ddata, dsz, sizeof(*ddata), int_compare)) {
/* this tag is part of the dont list: don't collect */
continue;
}
gmshModelMeshGetNodesByElementType(type, &nodeTags, &nodeTags_n,
- &coord, &coord_n, &pCoord, &pCoord_n, data[i], 0, &ierr);
+ &coord, &coord_n, &pCoord, &pCoord_n, tag, 0, &ierr);
ERR(gmsh_err_to_res_T(ierr));
- 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]));
+ ERR(darray_double_reserve(triangles, tcount));
+ for(j = 0; j < coord_n; j += 9) {
+#define PUSH3(A, D) { \
+ ERR(darray_double_push_back((A), (D)+0)); \
+ ERR(darray_double_push_back((A), (D)+1)); \
+ ERR(darray_double_push_back((A), (D)+2)); \
+}
+ /* Keep the triangle; if its normal is reversed, change vertices' order */
+ PUSH3(triangles, coord+j+0);
+ PUSH3(triangles, coord+j+3);
+ PUSH3(triangles, coord+j+6);
}
+#undef PUSH3
gmshFree(nodeTags); nodeTags = NULL;
gmshFree(coord); coord = NULL;
gmshFree(pCoord); pCoord = NULL;
}
ASSERT(tcount == darray_double_size_get(triangles));
- tcount /= 9;
+ if(count0 == tcount) {
+ log_message(dev, "No triangle collected for geometry '%s'.\n",
+ str_cget(&geometry->name));
+ }
exit:
if(!allocator) {
@@ -404,15 +447,243 @@ error:
goto exit;
}
+static INLINE void
+get_indices(const unsigned itri, unsigned ids[3], void* context)
+{
+ const double* ctx = context;
+ ASSERT(ids && ctx); (void)ctx;
+ ids[0] = itri * 3 + 0;
+ ids[1] = itri * 3 + 1;
+ ids[2] = itri * 3 + 2;
+}
+
+static INLINE void
+get_position(const unsigned ivert, double pos[3], void* context)
+{
+ const double* ctx = context;
+ ASSERT(pos && ctx);
+ pos[0] = ctx[ivert * 3 + 0];
+ pos[1] = ctx[ivert * 3 + 1];
+ pos[2] = ctx[ivert * 3 + 2];
+}
+
+static res_T
+scad_stl_sort_orientation
+ (struct darray_double* triangles,
+ const char* set_name,
+ const enum scad_normals_orientation orientation)
+{
+ res_T res = RES_OK;
+ struct scad_device* dev = get_device();
+ struct mem_allocator* allocator = dev->allocator;
+ struct logger* logger = dev->logger;
+ double* coord;
+ size_t coord_n;
+ unsigned i, ecount, tcount_in, vcount_in, tcount, vcount;
+ struct sg3d_device* sg3d = NULL;
+ struct sg3d_geometry* geom = NULL;
+ struct senc3d_device* senc3d = NULL;
+ struct senc3d_scene* senc3d_scn = NULL;
+ struct senc3d_enclosure* enclosure = NULL;
+ struct senc3d_enclosure_header header;
+ int convention, initialized = 0;
+ struct sg3d_geometry_add_callbacks callbacks = SG3D_ADD_CALLBACKS_NULL__;
+ struct darray_double new_triangles;
+ char *process = NULL, *contact_0 = NULL;
+ unsigned ocount = 0, process_count = 0;
+
+ if(!triangles || !set_name) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ coord_n = darray_double_size_get(triangles);
+ if(coord_n % 9 || coord_n > UINT_MAX) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+ if(coord_n == 0 || orientation == Scad_keep_normals_unchanged) {
+ goto exit;
+ }
+
+ tcount_in = (unsigned)(coord_n/9);
+ vcount_in = tcount_in * 3;
+ coord = darray_double_data_get(triangles);
+
+ ERR(sg3d_device_create(logger, allocator, dev->verbose, &sg3d));
+ ERR(sg3d_geometry_create(sg3d, &geom));
+
+ callbacks.get_indices = get_indices;
+ callbacks.get_position = get_position;
+
+ ERR(sg3d_geometry_reserve(geom, vcount_in, tcount_in, 0));
+ ERR(sg3d_geometry_add(geom, vcount_in, tcount_in, &callbacks, coord));
+
+ ERR(sg3d_geometry_get_unique_vertices_count(geom, &vcount));
+ ERR(sg3d_geometry_get_unique_triangles_count(geom, &tcount));
+ if(tcount != tcount_in) {
+ ASSERT(tcount_in > tcount);
+ log_warning(get_device(),
+ "Triangles duplicates were found when sorting out normals (%u / %u) "
+ "in set '%s'.\n",
+ tcount_in - tcount, tcount_in, set_name);
+ }
+ if(orientation == Scad_force_normals_outward)
+ convention = SENC3D_CONVENTION_NORMAL_BACK | SENC3D_CONVENTION_NORMAL_OUTSIDE;
+ else
+ convention = SENC3D_CONVENTION_NORMAL_BACK | SENC3D_CONVENTION_NORMAL_INSIDE;
+
+ ERR(senc3d_device_create(logger, allocator,
+ dev->options.Geometry.OCCParallel ? SENC3D_NTHREADS_DEFAULT : 1,
+ dev->verbose, &senc3d));
+ ERR(senc3d_scene_create(senc3d, convention,
+ tcount, sg3d_sencXd_geometry_get_indices, NULL,
+ vcount, sg3d_sencXd_geometry_get_position, geom, &senc3d_scn));
+
+ ERR(senc3d_scene_get_overlapping_triangles_count(senc3d_scn, &ocount));
+ if(ocount) {
+ logger_print(logger, LOG_ERROR,
+ "Overlapping triangles where found when sorting out normals (%u / %u) "
+ "in set '%s'.\n",
+ tcount_in - ocount, tcount_in, set_name);
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ ERR(senc3d_scene_get_enclosure_count(senc3d_scn, &ecount));
+ if(ecount < 2) {
+ /* Must define a closed volume */
+ log_error(get_device(),
+ "Triangle set '%s' doesn't define a closed volume.\n", set_name);
+ res = RES_BAD_ARG;
+ goto error;
+ } else {
+ /* Enclosure 0 is allways present and represents the outside of the whole
+ * scene. Enclosures with rank 1+ are closed enclosures: normal orientation
+ * makes sense there.
+ * We will only process triangles that limit the enclosures that have
+ * enclosure 0 on the other side. If we processed any enclosure, possible
+ * internal enclosures would trigger a double processing of some triangles.
+ * Finally, no enclosure is allowed other than 1) enclosures in contact with
+ * enclosure 0, or 2) enclosures in contact with enclosures in 1). */
+ unsigned e, enclosures[2];
+ darray_double_init(allocator, &new_triangles);
+ initialized = 1;
+ process = MEM_CALLOC(allocator, ecount, sizeof(*process));
+ contact_0 = MEM_CALLOC(allocator, ecount, sizeof(*contact_0));
+ if(!process || !contact_0) {
+ res = RES_MEM_ERR;
+ goto error;
+ }
+ for(e = 0; e < ecount; e++) {
+ ERR(senc3d_scene_get_enclosure(senc3d_scn, e, &enclosure));
+ ERR(senc3d_enclosure_get_header(enclosure, &header));
+ if(header.primitives_count != header.unique_primitives_count) {
+ log_error(get_device(),
+ "Triangle set '%s' define an invalid closed volume "
+ "(both sides of a triangle are in).\n",
+ set_name);
+ res = RES_BAD_ARG;
+ goto error;
+ }
+ if(e == 0) {
+ ERR(senc3d_enclosure_ref_put(enclosure));
+ enclosure = NULL;
+ continue;
+ }
+ for(i = 0; i < header.unique_primitives_count; i++) {
+ enum senc3d_side side;
+ unsigned gid;
+ ERR(senc3d_enclosure_get_triangle_id(enclosure, i, &gid, &side));
+ ERR(senc3d_scene_get_triangle_enclosures(senc3d_scn, gid, enclosures));
+ if(enclosures[0] == 0 || enclosures[1] == 0) {
+ process_count++;
+ process[e] = 1;
+ contact_0[e] = 1;
+ break;
+ }
+ }
+ ERR(senc3d_enclosure_ref_put(enclosure));
+ enclosure = NULL;
+ }
+ for(e = 1; e < ecount; e++) {
+ if(process[e]) continue;
+ ERR(senc3d_scene_get_enclosure(senc3d_scn, e, &enclosure));
+ ERR(senc3d_enclosure_get_header(enclosure, &header));
+ for(i = 0; i < header.unique_primitives_count; i++) {
+ enum senc3d_side side;
+ unsigned gid;
+ ERR(senc3d_enclosure_get_triangle_id(enclosure, i, &gid, &side));
+ ERR(senc3d_scene_get_triangle_enclosures(senc3d_scn, gid, enclosures));
+ if(contact_0[enclosures[0]] || contact_0[enclosures[1]]) {
+ process_count++;
+ process[e] = 1;
+ break;
+ }
+ }
+ ERR(senc3d_enclosure_ref_put(enclosure));
+ enclosure = NULL;
+ }
+ if(process_count != ecount -1) {
+ log_error(get_device(),
+ "Triangle set '%s' defines a topology that doesn't allow forcing normals.\n",
+ set_name);
+ res = RES_BAD_ARG;
+ goto error;
+ }
+ /* Just a guess */
+ ERR(darray_double_reserve(&new_triangles, 9 * tcount_in));
+ for(e = 1; e < ecount; e++) {
+ ERR(senc3d_scene_get_enclosure(senc3d_scn, e, &enclosure));
+ ERR(senc3d_enclosure_get_header(enclosure, &header));
+ for(i = 0; i < header.unique_primitives_count; i++) {
+ unsigned n, k, vrt[3];
+ /* Get the vertices as they are ordered in the enclosure's mesh */
+ ERR(senc3d_enclosure_get_triangle(enclosure, i, vrt));
+ /* Rewrite vertices according to enclosure's mesh */
+ for(n = 0; n < 3; n++) {
+ double pos[3];
+ ERR(senc3d_enclosure_get_vertex(enclosure, vrt[n], pos));
+ for(k = 0; k < 3; k++) {
+ ERR(darray_double_push_back(&new_triangles, pos+k));
+ }
+ }
+ }
+ ERR(senc3d_enclosure_ref_put(enclosure));
+ enclosure = NULL;
+ }
+ ERR(darray_double_copy_and_release(triangles, &new_triangles));
+ initialized = 0;
+ }
+
+exit:
+ MEM_RM(allocator, process);
+ MEM_RM(allocator, contact_0);
+ if(initialized) darray_double_release(&new_triangles);
+ if(sg3d) SG3D(device_ref_put(sg3d));
+ if(geom) SG3D(geometry_ref_put(geom));
+ if(senc3d) SENC3D(device_ref_put(senc3d));
+ if(senc3d_scn) SENC3D(scene_ref_put(senc3d_scn));
+ if(enclosure) SENC3D(enclosure_ref_put(enclosure));
+ return res;
+error:
+ goto exit;
+}
+
res_T
scad_stl_data_write
- (struct darray_double* triangles,
+ (const struct darray_double* triangles,
const char* filename,
+ const enum scad_normals_orientation orientation,
const int binary)
{
res_T res = RES_OK;
- size_t coord_n;
+ res_T tmp_res = RES_OK;
+ struct scad_device* dev = get_device();
+ struct darray_double sorted;
+ int initialized = 0;
double* coord;
+ size_t coord_n;
if(!triangles || !filename) {
res = RES_BAD_ARG;
@@ -424,12 +695,21 @@ scad_stl_data_write
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));
+ darray_double_init(dev->allocator, &sorted);
+ initialized = 1;
+ ERR(darray_double_copy(&sorted, triangles));
+
+ /* If sort_orientation fails, try to write the file anyway to allow debugging */
+ tmp_res = scad_stl_sort_orientation(&sorted, filename, orientation);
+ coord = darray_double_data_get(&sorted);
+ coord_n = darray_double_size_get(triangles);
+ if(binary) ERR(write_binary_stl(filename, coord, coord_n/9));
+ else ERR(write_ascii_stl(filename, coord, coord_n/9));
+ ERR(tmp_res);
exit:
+ if(initialized) darray_double_release(&sorted);
return res;
error:
goto exit;
@@ -438,7 +718,8 @@ error:
res_T
scad_stl_export
(struct scad_geometry* geometry,
- const char* prefix,
+ const char* file_name,
+ const enum scad_normals_orientation orientation,
const int binary)
{
res_T res = RES_OK;
@@ -447,8 +728,8 @@ scad_stl_export
struct str filename;
int initialized = 0;
- if(!geometry || (!prefix && str_is_empty(&geometry->name))) {
- res= RES_BAD_ARG;
+ if(!geometry || (!file_name && str_is_empty(&geometry->name))) {
+ res = RES_BAD_ARG;
goto error;
}
@@ -457,14 +738,18 @@ scad_stl_export
darray_double_init(dev->allocator, &trg);
str_init(dev->allocator, &filename);
initialized = 1;
- if(prefix) {
- ERR(str_set(&filename, prefix));
+ if(file_name) {
+ ERR(str_set(&filename, file_name));
} else {
+ if(str_is_empty(&geometry->name)) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
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));
+ ERR(scad_stl_data_write(&trg, str_cget(&filename), orientation, binary));
exit:
if(initialized) {
@@ -481,7 +766,8 @@ scad_stl_export_partial
(struct scad_geometry* geometry,
struct scad_geometry** dont,
const size_t dont_count,
- const char* prefix,
+ const char* file_name,
+ const enum scad_normals_orientation orientation,
const int binary)
{
res_T res = RES_OK;
@@ -491,7 +777,7 @@ scad_stl_export_partial
int initialized = 0;
if(!geometry) {
- res= RES_BAD_ARG;
+ res = RES_BAD_ARG;
goto error;
}
@@ -500,10 +786,10 @@ scad_stl_export_partial
darray_double_init(dev->allocator, &trg);
str_init(dev->allocator, &filename);
initialized = 1;
- if(prefix) {
- ERR(str_set(&filename, prefix));
+ if(file_name) {
+ ERR(str_set(&filename, file_name));
} else {
- if(str_len(&geometry->name) == 0) {
+ if(str_is_empty(&geometry->name)) {
res = RES_BAD_ARG;
goto error;
}
@@ -511,7 +797,7 @@ scad_stl_export_partial
}
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));
+ ERR(scad_stl_data_write(&trg, str_cget(&filename), orientation, binary));
exit:
if(initialized) {
@@ -526,7 +812,8 @@ error:
res_T
scad_stl_export_split
(struct scad_geometry* geometry,
- const char* prefix,
+ const char* file_name,
+ const enum scad_normals_orientation orientation,
const int binary)
{
size_t i;
@@ -536,21 +823,24 @@ scad_stl_export_split
res_T res = RES_OK;
(void)binary;
- if(!geometry || (!prefix && str_is_empty(&geometry->name))) {
+ if(!geometry || (!file_name && str_is_empty(&geometry->name))) {
res = RES_BAD_ARG;
goto error;
}
ERR(check_device(FUNC_NAME));
- ERR(scad_geometry_explode(geometry, prefix, &parts, &count));
+ ERR(scad_geometry_explode(geometry, file_name, &parts, &count));
ASSERT(count*2 == geometry->gmsh_dimTags_n);
for(i = 0; i < count; i++) {
- ERR(scad_stl_export(parts[i], NULL, binary));
+ ERR(scad_stl_export(parts[i], NULL, orientation, binary));
}
exit:
- MEM_RM(dev->allocator, parts);
+ if(parts) {
+ for(i = 0; i < count; i++) SCAD(geometry_ref_put(parts[i]));
+ MEM_RM(dev->allocator, parts);
+ }
return res;
error:
goto exit;
@@ -573,24 +863,3 @@ exit:
error:
goto exit;
}
-
-res_T
-scad_scene_partition
- (void)
-{
- int ierr = 0;
- res_T res = RES_OK;
-
- ERR(check_device(FUNC_NAME));
-
- gmshModelOccRemoveAllDuplicates(&ierr);
- ERR(gmsh_err_to_res_T(ierr));
-
- gmshModelOccSynchronize(&ierr);
- ERR(gmsh_err_to_res_T(ierr));
-
-exit:
- return res;
-error:
- goto exit;
-}
diff --git a/src/scad.h b/src/scad.h
@@ -78,9 +78,10 @@ enum scad_stl_solids {
};
enum scad_log_refcounting {
- Scad_log_none,
- Scad_log_only_undeleted,
- Scad_log_all
+ Scad_log_none = 0,
+ Scad_log_dimTags_only_undeleted = BIT(0),
+ Scad_log_dimTags_all = BIT(1),
+ Scad_log_geometry = BIT(2)
};
/* A type to specify options for the gmsh library */
@@ -106,21 +107,34 @@ struct scad_options {
struct {
int Step; /* Run UI when entering any scad API function; requires a FLTK-enabled gmsh build */
int SynchronizeOnRunUI;
- enum scad_log_refcounting LogOpenCascadeTagsRefCounting;
+ enum scad_log_refcounting LogRefCounting;
int DebugOpenCascadeSync; /* Systematic call to synchronize; if results change there is a sync bug in star-cad! */
} Misc;
};
#define SCAD_DEFAULT_OPTIONS__ \
- { { Scad_frontal_Delaunay, Scad_surfaces_and_volumes, 1, 36, 1, 1e+22, 0, \
+ { { Scad_frontal_Delaunay, Scad_surfaces_and_volumes, 1, 36, 1, 1e+22, 1e-6, \
1, Scad_one_solid_per_physical_surface }, \
{ Scad_verbosity_errors, 1 }, \
{ 1 }, \
- { 0, Scad_log_none, 0, 0 } \
+ { 0, 0, Scad_log_none, 0 } \
}
static const struct scad_options SCAD_DEFAULT_OPTIONS = SCAD_DEFAULT_OPTIONS__;
+/* A type to specify what to swap in geometries_swap calls */
+enum scad_swap_elements {
+ Scad_swap_name = BIT(0),
+ Scad_swap_geometry = BIT(1)
+};
+
+/* A type to specify normals' orientation when writing STL files. */
+enum scad_normals_orientation {
+ Scad_keep_normals_unchanged, /* The only one allowed with non closed shapes */
+ Scad_force_normals_outward,
+ Scad_force_normals_inward
+};
+
BEGIN_DECLS
/*******************************************************************************
@@ -145,17 +159,10 @@ SCAD_API res_T
scad_set_options
(const struct scad_options* options); /* May be NULL: set default */
-/* Explicitly synchronize the current state with regard to recent geometry changes.
- * Synchronize calls should be automatically triggered when needed.
- * Only provided as a way to check for auto-synchronize bugs! */
-SCAD_API res_T
-scad_synchronize
- (void);
-
/*******************************************************************************
* Geometry API - A geometry is a primitive, a group of primitives, or the
* result of an operation on geometries.
- * If provided, names must be unique.
+ * If provided, name must be unique.
******************************************************************************/
SCAD_API res_T
@@ -176,6 +183,22 @@ scad_geometry_get_count
(const struct scad_geometry* geom,
size_t* count);
+/* Attach some custom data `data' to geometry `geom'.
+ * If provided, release() is called when `geom' is released or if
+ * set_custom_data is called again. */
+SCAD_API res_T
+scad_geometry_set_custom_data
+ (struct scad_geometry* geom,
+ void (*release) (void* data), /* Can be NULL */
+ void* data); /* Can be NULL */
+
+/* Get the custom data attached to geometry `geom'.
+ * If set_custom_data has not been called before, return NULL. */
+SCAD_API res_T
+scad_geometry_get_custom_data
+ (struct scad_geometry* geom,
+ void** data);
+
/* Get a pointer to `geom's name.
* Note that this reference is only valid during the lifetime of `geom' (don't
* use name after deleting `geom') */
@@ -184,11 +207,14 @@ scad_geometry_get_name
(const struct scad_geometry* geom,
const char** name);
-/* Swap names of `geom1' and `geom2' */
+/* Swap the internals of geometry pools (swap pool1[i] and pool2[i]); what is
+ * swapped is set usig flags. Pools must have the same count. */
SCAD_API res_T
-scad_geometry_swap_names
- (struct scad_geometry* geom1,
- struct scad_geometry* geom2);
+scad_geometries_swap
+ (struct scad_geometry** pool1,
+ struct scad_geometry** pool2,
+ const size_t count,
+ const int flags);
/* Get the `mass' of the geometry `geom'. It means area for a 2D geometry and
* volume for a 3D geometry. */
@@ -262,6 +288,34 @@ scad_add_sphere
const double radius,
struct scad_geometry** sphere);
+/* Check if geometries `geom1' and `geom2' have the same content, that is:
+ * - are the same scad_geometries (trivial case),
+ * - contain the same internal entities.
+ * To check if 2 geometries are "equivalent", one as to apply boolean operators
+ * (e.g. cut) and check the result accordingly (e.g. empty result). */
+SCAD_API res_T
+scad_geometries_equal
+ (struct scad_geometry* geom1,
+ struct scad_geometry* geom2,
+ int* equal);
+
+/* Check if all the entities of `geometry' are part of the geometries in
+ * `geometries'. */
+SCAD_API res_T
+scad_geometry_is_included
+ (struct scad_geometry* geometry,
+ struct scad_geometry** geometries,
+ const size_t geometries_count,
+ int* included);
+
+/* Create a new geometry made from all the entities from `geometries'. */
+SCAD_API res_T
+scad_collect_geometries
+ (const char* name, /* Can be NULL */
+ struct scad_geometry** geometries,
+ const size_t geometries_count,
+ struct scad_geometry** out_geometry);
+
/* Compute the boolean union (the fusion) of the geometries in `geometries' and
* `tools'. */
SCAD_API res_T
@@ -309,9 +363,7 @@ scad_geometries_common_boundaries
/* Compute the boolean fragments (general fuse) resulting from the
* intersection of the geometries in `geometries', making all interfaces
* conformal.
- * If overlapping is allowed `out_geometries' is constructed and the new
- * geometries are created unnamed; if overlapping is not allowed
- * `out_geometries' remains unchanged and can be NULL.
+ * The output geometries are created unnamed.
* 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. */
@@ -320,22 +372,7 @@ scad_geometries_partition
(struct scad_geometry** geometries,
const size_t geometries_count,
const int allow_overlapping,
- struct scad_geometry** out_geometries); /* Can be NULL if overlapping disallowed */
-
-/* Partition the whole scene.
- * Overlapping is not detected and newly created objects are not returned. */
-SCAD_API res_T
-scad_scene_partition
- (void);
-
-SCAD_API res_T
-scad_fragment_geometries
- (const char* name, /* Can be NULL */
- struct scad_geometry** geometries,
- const size_t geometries_count,
- struct scad_geometry** tools,
- const size_t tools_count,
- struct scad_geometry** out_geometry);
+ struct scad_geometry** out_geometries);
/* Get the boundary of the geometry `geom'. */
SCAD_API res_T
@@ -358,37 +395,48 @@ scad_geometry_rename
(struct scad_geometry* geom,
const char* name); /* Can be NULL */
-/* Reverse the orientation of the geomery `geome' */
+/* Scale the geometry by * factors `scale' along the three coordinate axes;
+ * use `center', as the center of the homothetic transformation. */
SCAD_API res_T
-scad_geometry_reverse
- (struct scad_geometry* geom);
+scad_geometry_dilate
+ (const struct scad_geometry* geom,
+ const double center[3],
+ const double scale[3],
+ const char* name, /* Can be NULL */
+ struct scad_geometry** out_geometry);
/* Translate the geometry `geom' along (`dx', `dy', `dz'). */
SCAD_API res_T
scad_geometry_translate
- (struct scad_geometry* geom,
- const double dxdydz[3]);
+ (const struct scad_geometry* geom,
+ const double dxdydz[3],
+ const char* name, /* Can be NULL */
+ struct scad_geometry** out_geometry);
/* Rotate the geometry `geom' by `angle' radians around the axis of revolution
* defined by the point `pt' and the direction `dir'. */
SCAD_API res_T
scad_geometry_rotate
- (struct scad_geometry* geom,
+ (const struct scad_geometry* geom,
const double pt[3],
const double dir[3],
- const double angle);
+ const double angle,
+ const char* name, /* Can be NULL */
+ struct scad_geometry** out_geometry);
/* Extrude the geometry `geom' using a translation along (`dx', `dy', `dz'). */
SCAD_API res_T
scad_geometry_extrude
- (const struct scad_geometry* geom,
+ (struct scad_geometry* geom,
const char* name, /* Can be NULL */
const double dxdydz[3],
struct scad_geometry** out_geometry);
/* Return a list of geometries which form the geometry `geom'.
- * Note that `out_geometries' is allocated using the allocator provided when
- * initializing star-cad and should be freed accordingly. */
+ * The output geometries are named <base>_<rank>, where <base> is either
+ * prefix_name or geom's name, and <rank> counting from 0.
+ * The result `out_geometries' being allocated using the allocator provided when
+ * initializing star-cad, it should be freed accordingly. */
SCAD_API res_T
scad_geometry_explode
(const struct scad_geometry* geom,
@@ -407,70 +455,79 @@ scad_step_import
struct scad_geometry*** out_geometries,
size_t* out_geometry_n);
-/* Export the geometry `geom' to an STL file.
+/* Export the mesh of 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),
+ * If `file_name' 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. */
+ * file_name nor the geometry name is defined). Mesh orientation can be forced
+ * inward or outward only if it defines a closed volume. The file format is
+ * either binary or ascii, depending on the value of the `binary' argument. */
SCAD_API res_T
scad_stl_export
(struct scad_geometry* geom,
- const char* prefix, /* Can be NULL if geometry was named: use geometry name */
+ const char* file_name,
+ const enum scad_normals_orientation orientation,
const int binary); /* File format */
-/* Same as previous, but geometries that are part of `exclude' is not exported */
+/* Same as previous, but geometries that are part of `exclude' are 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 char* file_name,
+ const enum scad_normals_orientation orientation,
const int binary);
-/* Export the geometry `geom' in as many files than its count. */
+/* Export the geometry `geom' in as many files than its count.
+ * The files are named <base>_<rank>.stl, where <base> is either file_name or
+ * geom's name, and <rank> counting from 0. */
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 char* file_name,
+ const enum scad_normals_orientation orientation,
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. */
+/* Accumulate the mesh of the geometry `geom' into `triangles', each triangle
+ * being described by 9 doubles in a STL way.
+ * In order to get a mesh, one has to call scad_scene_mesh first. */
SCAD_API res_T
scad_stl_get_data
(struct scad_geometry* geom,
- struct darray_double* triangles); /* Can contain some triangles already */
+ struct darray_double* triangles);
-/* Same as previous, but geometries that are part of `exclude' is not collected */
+/* Same as previous, but geometries in `exclude', that can be 2D and/or 3D, are
+ * 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 */
+ struct darray_double* triangles);
-/* Write the mesh the same way stl_export do, using data as returned by
- * stl_get_data[_partial] */
+/* Write a mesh the same way stl_export do, using `triangles' as returned by
+ * stl_get_data[_partial]. */
SCAD_API res_T
scad_stl_data_write
- (struct darray_double* triangles,
+ (const struct darray_double* triangles,
const char* filename,
+ const enum scad_normals_orientation orientation,
const int binary);
+/* Write the whole scene in a format that depends on the file name extension. */
SCAD_API res_T
scad_scene_write
(const char* name);
-SCAD_API res_T /* FIXME remove this */
-scad_run_ui
- (void);
-
+/* Create the mesh of the whole scene. */
SCAD_API res_T
scad_scene_mesh
(void);
+/* Get the normal of the geometry `geom' at position `p'.
+ * The normal is set in `N' and the underlying 2D entity to which `p' belongs is
+ * returned as a new geometry in `out_geometry'. */
SCAD_API res_T
scad_geometry_normal
(struct scad_geometry* geom,
@@ -479,15 +536,46 @@ scad_geometry_normal
const char* name, /* Can be NULL */
struct scad_geometry** out_geometry);
-/* Scale the geometry by * factors `scale' along the three coordinate axes;
- * use `center', as the center of the homothetic transformation. */
+/* The following API calls are meant for debugging purposes.
+ * They can be called from gdb. */
+
+/* Open gmsh in GUI mode so that the model can be inspected and even modified.
+ * To use it from gdb:
+ * (gdb) call scad_run_ui()
+ * (then close gmsh and gdb is back to the breakpoint). */
SCAD_API res_T
-scad_geometry_dilate
- (struct scad_geometry* geom,
- double center[3],
- double scale[3]);
+scad_run_ui
+ (void);
+/* Explicitly synchronize the current state with regard to recent geometry changes.
+ * Note however that synchronize calls should be automatically triggered when
+ * needed.
+ * To use it from gdb:
+ * (gdb) call scad_synchronize()
+ * Only provided as a way to check for auto-synchronize bugs! */
+SCAD_API res_T
+scad_synchronize
+ (void);
+/* Get the refcount, as managed by star-cad to trigger remove calls on dim.tag
+ * OCC internal entities. Return SIZE_MAX if dim.tag is not valid in the OCC
+ * context.
+ * To use it from gdb:
+ * (gdb) call scad_get_dimtag_refcount(3, 1)
+ * $5 = 7
+ */
+SCAD_API size_t
+scad_get_dimtag_refcount
+ (const int dim,
+ const int tag);
+
+/* Dump all the geometries with address/name, ref count and tags.
+ * To use it from gdb:
+ * (gdb) call scad_dump_geometries()
+ */
+SCAD_API void
+scad_dump_geometries
+ (void);
END_DECLS
#endif /* SCAD_H */
diff --git a/src/scad_device.c b/src/scad_device.c
@@ -31,8 +31,61 @@
* Local functions
******************************************************************************/
static void
+device_release_tags_of_dim
+ (struct scad_device* dev,
+ const int dim)
+{
+ struct htable_tags2desc* table;
+ struct htable_tags2desc_iterator it, end;
+ int fst = 1;
+ int ierr;
+ ASSERT(dev);
+ CHK(dim == 2 || dim == 3); /* other dims not managed yet */
+
+ table = dev->tags2desc + dim - 2;
+ htable_tags2desc_begin(table, &it);
+ htable_tags2desc_end(table, &end);
+ while(!htable_tags2desc_iterator_eq(&it, &end)) {
+ int dt[2], tag = *htable_tags2desc_iterator_key_get(&it);
+ struct tag_desc* desc = device_get_description(dim, tag);
+ ASSERT(desc->refcount > 0);
+ htable_tags2desc_iterator_next(&it);
+ /* desc is a descriptor for a non-released tag */
+ if(fst) {
+ fst = 0;
+ logger_print(dev->logger, dev->log_type,
+ "Some tags were not removed properly.\n");
+ }
+ logger_print(dev->logger, dev->log_type, "Tag %d.%d (refcount = %lu).\n",
+ dim, tag, (long unsigned)desc->refcount);
+ /* Remove tag according to policy */
+ dt[0] = dim;
+ dt[1] = tag;
+ switch(desc->delete_policy) {
+ case Scad_do_not_delete:
+ logger_print(dev->logger, dev->log_type,
+ "Tag %d.%d not deleted due to policy.\n",
+ dim, tag);
+ break;
+ case Scad_delete_non_recursive:
+ logger_print(dev->logger, dev->log_type,
+ "Tag %d.%d non-recursively deleted due to policy.\n",
+ dim, tag);
+ gmshModelOccRemove(dt, 2, 0, &ierr);
+ break;
+ case Scad_delete_recursive:
+ gmshModelOccRemove(dt, 2, 1, &ierr);
+ break;
+ default: FATAL("Invalid enum value");
+ }
+ }
+ htable_tags2desc_release(table);
+}
+
+static res_T
device_release(struct scad_device* dev)
{
+ res_T res = RES_OK;
struct htable_geometries tmp;
struct htable_geometries_iterator it, end;
int log, empty;
@@ -41,10 +94,11 @@ device_release(struct scad_device* dev)
ASSERT(dev);
- option = dev->options.Misc.LogOpenCascadeTagsRefCounting;
+ option = dev->options.Misc.LogRefCounting;
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);
+ log = (option & Scad_log_dimTags_all)
+ || (!empty && (option & Scad_log_dimTags_only_undeleted));
dev->log = log;
dev->log_type = log_type;
@@ -63,14 +117,16 @@ device_release(struct scad_device* dev)
htable_geometries_iterator_next(&it);
}
htable_names_release(&dev->geometry_names);
- htable_tags2geom_release(&dev->tags2geom[0]);
- htable_tags2geom_release(&dev->tags2geom[1]);
htable_geometries_release(&dev->allgeom);
+ device_release_tags_of_dim(dev, 2);
+ device_release_tags_of_dim(dev, 3);
if(log) {
logger_print(dev->logger, log_type, "End finalizing scad.\n");
}
MEM_RM(dev->allocator, dev);
htable_geometries_release(&tmp);
+
+ return res;
}
void
@@ -157,126 +213,318 @@ device_register_tags
(struct scad_geometry* geom)
{
res_T res = RES_OK;
- int* dimTags;
- size_t count, i;
struct scad_device* dev = get_device();
- int log = (dev->options.Misc.LogOpenCascadeTagsRefCounting == Scad_log_all);
+ int log = (dev->options.Misc.LogRefCounting & Scad_log_dimTags_all);
ASSERT(geom);
- dimTags = geom->gmsh_dimTags;
- count = geom->gmsh_dimTags_n;
- if(log) {
+ if(geom->gmsh_dimTags_n) {
+ if(log) {
+ if(str_is_empty(&geom->name)) {
+ log_message(dev, "Registering tags for unnamed geometry %p.\n",
+ (void*)geom);
+ } else {
+ log_message(dev, "Registering tags for geometry '%s'.\n",
+ str_cget(&geom->name));
+ }
+ }
+ ERR(do_device_tags_ref_get(geom->gmsh_dimTags, geom->gmsh_dimTags_n));
+ }
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+struct tag_desc*
+device_get_description
+ (const int dim,
+ const int tag)
+{
+ struct scad_device* dev = get_device();
+ struct htable_tags2desc* t2d;
+ struct tag_desc* desc;
+ CHK(dim == 2 || dim == 3); /* other dims not managed yet */
+
+ t2d = g_device->tags2desc + (dim-2);
+ desc = htable_tags2desc_find(t2d, &tag);
+ if(!desc || desc->refcount == 0) {
+ logger_print(dev->logger, LOG_ERROR,
+ "SCAD internal error: tag %d.%d not registered.\n", dim, tag);
+ }
+ return desc;
+}
+
+void
+scad_dump_geometries
+ (void)
+{
+ struct scad_device* dev = get_device();
+ struct htable_geometries_iterator it, end;
+
+ if(!dev) {
+ printf("Error: star-cad is not initialized.\n");
+ return;
+ }
+ if(htable_geometries_is_empty(&dev->allgeom)) {
+ printf("No geometry defined.\n");
+ return;
+ }
+ htable_geometries_begin(&dev->allgeom, &it);
+ htable_geometries_end(&dev->allgeom, &end);
+ while(!htable_geometries_iterator_eq(&it, &end)) {
+ struct scad_geometry* geom = *htable_geometries_iterator_key_get(&it);
+ size_t i;
+ htable_geometries_iterator_next(&it);
if(str_is_empty(&geom->name)) {
- log_message(dev, "Registering tags for unnamed geometry %p.\n",
- (void*)geom);
+ printf("Unnamed geometry %p (count is %lu), tags: ",
+ (void*)geom, (long unsigned)geom->ref);
} else {
- log_message(dev, "Registering tags for geometry '%s'.\n",
- str_cget(&geom->name));
+ printf("Geometry '%s' (%p, count is %lu), tags: ",
+ str_cget(&geom->name), (void*)geom, (long unsigned)geom->ref);
}
+ for(i = 0; i < geom->gmsh_dimTags_n; i += 2) {
+ int dim = geom->gmsh_dimTags[i];
+ int tag = geom->gmsh_dimTags[i+1];
+ printf((i ? ", %d.%d" : "%d.%d"), dim, tag);
+ }
+ printf(".\n");
+ }
+}
+
+static void
+device_remove_description
+ (const int dim,
+ const int tag)
+{
+ struct scad_device* dev = get_device();
+ struct htable_tags2desc* t2d;
+ struct tag_desc* desc;
+ CHK(dim == 2 || dim == 3); /* other dims not managed yet */
+
+ t2d = g_device->tags2desc + (dim-2);
+ desc = htable_tags2desc_find(t2d, &tag);
+ if(!desc) {
+ logger_print(dev->logger, LOG_ERROR,
+ "SCAD internal error: tag %d.%d not registered.\n", dim, tag);
+ }
+ if(desc->refcount != 0) {
+ logger_print(dev->logger, LOG_ERROR,
+ "SCAD internal error: erasing tag %d.%d that still has references.\n",
+ dim, tag);
}
+ CHK(1 == htable_tags2desc_erase(t2d, &tag));
+}
+
+res_T
+do_device_tags_ref_get
+ (const int* dimTags,
+ const size_t count)
+{
+ res_T res = RES_OK;
+ size_t i;
+ struct scad_device* dev = get_device();
+ int log = (dev->options.Misc.LogRefCounting & Scad_log_dimTags_all);
+ enum log_type log_type = dev->log_type;
+
+ ASSERT(dimTags || count == 0);
for(i = 0; i < count; i += 2) {
int dim = dimTags[i];
int tag = dimTags[i+1];
- struct htable_tags2geom* t2g;
- struct htable_geometries* geoms;
- char one = 1;
+ struct htable_tags2desc* t2d;
+ struct tag_desc* desc;
CHK(dim == 2 || dim == 3); /* other dims not managed yet */
- /* Add geom to the geometries that use tag@dim */
- t2g = g_device->tags2geom + (dim-2);
- geoms = htable_tags2geom_find(t2g, &tag);
- if(!geoms) {
- /* First geom using this tag: create the table */
- struct htable_geometries g;
- htable_geometries_init(g_device->allocator, &g);
- ERR(htable_tags2geom_set(t2g, &tag, &g));
- geoms = htable_tags2geom_find(t2g, &tag);
- ASSERT(geoms);
+
+ t2d = dev->tags2desc + (dim-2);
+ desc = htable_tags2desc_find(t2d, &tag);
+ if(!desc) {
+ /* First ref to dim.tag: create the description */
+ struct tag_desc d;
+ tag_desc_init(dev->allocator, &d);
+ ERR(htable_tags2desc_set(t2d, &tag, &d));
if(log) {
- log_message(dev, "New dim %d tag %d (count set to 1).\n", dim, tag);
+ logger_print(dev->logger, log_type, "New tag %d.%d (count set to 1).\n",
+ dim, tag);
}
} else {
+ desc->refcount++;
if(log) {
- size_t n = htable_geometries_size_get(geoms);
- if(n > 0) {
- log_message(dev, "Dim %d tag %d (count increased to %lu).\n",
- dim, tag, n+1);
+ if(desc->refcount > 1) {
+ logger_print(dev->logger, log_type, "Tag %d.%d (count increased to %lu).\n",
+ dim, tag, desc->refcount);
} else {
- log_message(dev, "Reuse dim %d tag %d (count set to 1).\n", dim, tag);
+ logger_print(dev->logger, log_type, "Reuse tag %d.%d (count set to 1).\n",
+ dim, tag);
}
}
}
- ASSERT(!htable_geometries_find(geoms, &geom));
- ERR(htable_geometries_set(geoms, &geom, &one));
- ASSERT(htable_geometries_size_get(geoms) >= 1);
}
-end:
+exit:
return res;
error:
- goto end;
+ goto exit;
}
res_T
-device_unregister_tags
+do_device_tags_ref_put
(const int log,
const enum log_type log_type,
- struct scad_geometry* geom)
+ int* dimTags,
+ size_t count)
{
res_T res = RES_OK;
- int* dimTags;
- size_t count, i;
+ size_t i;
struct scad_device* dev = get_device();
+ struct str msg;
- ASSERT(geom);
-
- dimTags = geom->gmsh_dimTags;
- count = geom->gmsh_dimTags_n;
-
- if(log) {
- if(str_is_empty(&geom->name)) {
- logger_print(dev->logger, log_type,
- "Unregistering tags for unnamed geometry %p.\n", (void*)geom);
- } else {
- logger_print(dev->logger, log_type,
- "Unregistering tags for geometry '%s'.\n", str_cget(&geom->name));
- }
- }
+ ASSERT(dimTags || count == 0);
+ if(log) str_init(dev->allocator, &msg);
for(i = 0; i < count; i += 2) {
int dim = dimTags[i];
int tag = dimTags[i+1];
int ierr;
- struct htable_tags2geom* t2g;
- struct htable_geometries* geoms;
- size_t n;
- CHK(dim == 2 || dim == 3); /* other dims not managed yet */
- t2g = g_device->tags2geom + (dim-2);
- geoms = htable_tags2geom_find(t2g, &tag);
- n = htable_geometries_erase(geoms, &geom);
- ASSERT(geoms && n == 1); (void)n;
- n = htable_geometries_size_get(geoms);
- if(n > 0) {
+ struct tag_desc* desc = device_get_description(dim, tag);
+
+ if(!desc) {
+ res = RES_BAD_OP;
+ goto error;
+ }
+
+ /* Check if still in use after this unregistration */
+ desc->refcount--;
+ if(desc->refcount > 0) {
if(log) {
logger_print(dev->logger, log_type,
- "Dim %d tag %d (count decreased to %lu).\n", dim, tag, (unsigned long)n);
+ "Tag %d.%d (count decreased to %lu).\n",
+ dim, tag, (unsigned long)desc->refcount);
}
continue;
}
+
/* The gmsh geometry with tag 'tag' is not in use anymore: release it */
- if(log) {
- logger_print(dev->logger, log_type, "Dim %d tag %d removed.\n", dim, tag);
+ switch(desc->delete_policy) {
+ case Scad_do_not_delete:
+ if(log) {
+ logger_print(dev->logger, log_type,
+ "Tag %d.%d not deleted due to policy.\n",
+ dim, tag);
+ }
+ break;
+ case Scad_delete_non_recursive:
+ if(log) {
+ logger_print(dev->logger, log_type,
+ "Tag %d.%d non-recursively deleted due to policy.\n",
+ dim, tag);
+ }
+ gmshModelOccRemove(dimTags+i, 2, 0, &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+ break;
+ case Scad_delete_recursive:
+ if(log) {
+ logger_print(dev->logger, log_type, "Tag %d.%d recursively deleted.\n",
+ dim, tag);
+ }
+ gmshModelOccRemove(dimTags+i, 2, 1, &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+ break;
+ default: FATAL("Invalid enum value");
}
+ /* Release associated tags in tags_to_refput */
+ if(darray_int_size_get(&desc->tags_to_refput)) {
+ size_t j;
+ const int* dt = darray_int_cdata_get(&desc->tags_to_refput);
+ if(log) {
+ ERR(str_set(&msg, "Putting a reference to tags: "));
+ for(j = 0; j < darray_int_size_get(&desc->tags_to_refput); j += 2) {
+ int d = dt[j];
+ int t = dt[j+1];
+ ERR(str_append_printf(&msg, (j ? ", %d.%d" : "%d.%d"), d, t));
+ }
+ logger_print(dev->logger, log_type, "%s\n", str_cget(&msg));
+ }
+ ERR(do_device_tags_ref_put(log, log_type,
+ darray_int_data_get(&desc->tags_to_refput),
+ darray_int_size_get(&desc->tags_to_refput)));
+ }
+ device_remove_description(dim, tag);
+ }
+
+exit:
+ if(log) str_release(&msg);
+ return res;
+error:
+ goto exit;
+}
+
+res_T
+device_register_ref_to_tags
+ (const int dim,
+ const int tag,
+ const int* dimTags,
+ const size_t count)
+{
+ res_T res = RES_OK;
+ struct tag_desc* desc = device_get_description(dim, tag);
+ size_t i, prev_count, c = 0;
+ const int* dt;
+ ASSERT(dimTags);
- gmshModelOccRemove(dimTags+i, 2, 1, &ierr);
- ERR(gmsh_err_to_res_T(ierr));
+ if(!desc) {
+ res = RES_BAD_OP;
+ goto error;
}
-end:
+ prev_count = darray_int_size_get(&desc->tags_to_refput);
+ ERR(darray_int_reserve(&desc->tags_to_refput, count + prev_count));
+ for(i = 0; i < count; i += 2) {
+ int d = dimTags[i];
+ int t = dimTags[i+1];
+ if(d == dim && t == tag) continue;
+ ERR(darray_int_push_back(&desc->tags_to_refput, &d));
+ ERR(darray_int_push_back(&desc->tags_to_refput, &t));
+ c += 2;
+ }
+ /* As refences will be put, need to get them now */
+ dt = darray_int_cdata_get(&desc->tags_to_refput);
+ ERR(do_device_tags_ref_get(dt + prev_count, c));
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+res_T
+device_unregister_tags
+ (const int log,
+ const enum log_type log_type,
+ struct scad_geometry* geom)
+{
+ res_T res = RES_OK;
+ struct scad_device* dev = get_device();
+ ASSERT(geom);
+
+ if(log) {
+ if(str_is_empty(&geom->name)) {
+ logger_print(dev->logger, log_type,
+ "Unregistering tags for unnamed geometry %p.\n", (void*)geom);
+ } else {
+ logger_print(dev->logger, log_type,
+ "Unregistering tags for geometry '%s'.\n", str_cget(&geom->name));
+ }
+ }
+
+ ERR(do_device_tags_ref_put(log, log_type, geom->gmsh_dimTags,
+ geom->gmsh_dimTags_n));
+
+exit:
return res;
error:
- goto end;
+ goto exit;
}
/*******************************************************************************
@@ -318,12 +566,11 @@ scad_initialize
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);
+ g_device->log = (g_device->options.Misc.LogRefCounting != Scad_log_none);
htable_names_init(allocator, &g_device->geometry_names);
htable_geometries_init(allocator, &g_device->allgeom);
- htable_tags2geom_init(allocator, &g_device->tags2geom[0]);
- htable_tags2geom_init(allocator, &g_device->tags2geom[1]);
+ htable_tags2desc_init(allocator, &g_device->tags2desc[0]);
+ htable_tags2desc_init(allocator, &g_device->tags2desc[1]);
/* Init to default */
scad_set_options(NULL);
@@ -341,7 +588,7 @@ res_T
scad_finalize
(void)
{
- res_T res = RES_OK;
+ res_T tmp_res = RES_OK, res = RES_OK;
int ierr;
struct scad_device* dev = get_device();
int log, empty;
@@ -349,22 +596,24 @@ scad_finalize
enum log_type log_type;
ERR(check_device(FUNC_NAME));
- option = dev->options.Misc.LogOpenCascadeTagsRefCounting;
+ option = dev->options.Misc.LogRefCounting;
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);
+ log = (option & Scad_log_dimTags_all)
+ || (!empty && (option & Scad_log_dimTags_only_undeleted));
if(log) {
logger_print(dev->logger, log_type,
"Finalizing scad; undeleted tags will be automatically unregistered.\n");
}
- device_release(g_device);
+ tmp_res = device_release(g_device);
g_device = NULL;
gmshFinalize(&ierr);
ERR(gmsh_err_to_res_T(ierr));
exit:
+ if(tmp_res != RES_OK) res = tmp_res;
return res;
error:
goto exit;
@@ -423,12 +672,16 @@ scad_set_options
/* Check non-gmsh option validity if user-provided */
(void)actual_options->Misc.Step; /* int boolean: always OK */
(void)actual_options->Misc.SynchronizeOnRunUI; /* int boolean: always OK */
- (void)actual_options->Misc.LogOpenCascadeTagsRefCounting; /* int boolean: always OK */
+ (void)actual_options->Misc.LogRefCounting; /* int boolean: always OK */
(void)actual_options->Misc.DebugOpenCascadeSync; /* int boolean: always OK */
}
dev->options = *actual_options;
+ /* Update logging policy */
+ dev->log
+ = (dev->options.Misc.LogRefCounting & Scad_log_dimTags_all);
+
exit:
return res;
error:
diff --git a/src/scad_device.h b/src/scad_device.h
@@ -23,6 +23,8 @@
#include <rsys/ref_count.h>
#include <rsys/logger.h>
#include <rsys/hash_table.h>
+#include <rsys/dynamic_array.h>
+#include <rsys/dynamic_array_int.h>
#include <rsys/str.h>
static INLINE char
@@ -38,7 +40,6 @@ hash_str(const struct str* a)
}
#define HTABLE_NAME names
-#define HTABLE_DATA struct scad_geometry*
#define HTABLE_KEY struct str
#define HTABLE_KEY_FUNCTOR_INIT str_init
#define HTABLE_KEY_FUNCTOR_RELEASE str_release
@@ -46,20 +47,72 @@ hash_str(const struct str* a)
#define HTABLE_KEY_FUNCTOR_COPY_AND_RELEASE str_copy_and_release
#define HTABLE_KEY_FUNCTOR_EQ eq_str
#define HTABLE_KEY_FUNCTOR_HASH hash_str
+#define HTABLE_DATA struct scad_geometry*
#include <rsys/hash_table.h>
#define HTABLE_NAME geometries
-#define HTABLE_DATA char
#define HTABLE_KEY struct scad_geometry*
+#define HTABLE_DATA char
#include <rsys/hash_table.h>
-#define HTABLE_NAME tags2geom
-#define HTABLE_DATA struct htable_geometries
+enum delete_policy {
+ Scad_delete_recursive,
+ Scad_delete_non_recursive,
+ Scad_do_not_delete
+};
+
+struct tag_desc {
+ enum delete_policy delete_policy;
+ size_t refcount;
+ struct darray_int tags_to_refput;
+};
+
+static INLINE void
+tag_desc_init
+ (struct mem_allocator* allocator, /* May be NULL <=> use default allocator */
+ struct tag_desc* data)
+{
+ ASSERT(data);
+ data->delete_policy = Scad_delete_recursive;
+ data->refcount = 1;
+ darray_int_init(allocator, &data->tags_to_refput);
+}
+static INLINE void
+tag_desc_release
+ (struct tag_desc* data)
+{
+ ASSERT(data);
+ darray_int_release(&data->tags_to_refput);
+}
+static FINLINE res_T
+tag_desc_copy
+ (struct tag_desc* dst,
+ struct tag_desc const* src)
+{
+ ASSERT(dst && src);
+ dst->delete_policy = src->delete_policy;
+ dst->refcount = src->refcount;
+ darray_int_copy(&dst->tags_to_refput, &src->tags_to_refput);
+ return RES_OK;
+}
+static FINLINE res_T
+tag_desc_copy_and_release
+ (struct tag_desc* dst,
+ struct tag_desc* src)
+ {
+ ASSERT(dst && src);
+ dst->delete_policy = src->delete_policy;
+ dst->refcount = src->refcount;
+ darray_int_copy_and_release(&dst->tags_to_refput, &src->tags_to_refput);
+ return RES_OK;
+}
+#define HTABLE_NAME tags2desc
#define HTABLE_KEY int
-#define HTABLE_DATA_FUNCTOR_INIT htable_geometries_init
-#define HTABLE_DATA_FUNCTOR_RELEASE htable_geometries_release
-#define HTABLE_DATA_FUNCTOR_COPY htable_geometries_copy
-#define HTABLE_DATA_FUNCTOR_COPY_AND_RELEASE htable_geometries_copy_and_release
+#define HTABLE_DATA struct tag_desc
+#define HTABLE_DATA_FUNCTOR_INIT tag_desc_init
+#define HTABLE_DATA_FUNCTOR_RELEASE tag_desc_release
+#define HTABLE_DATA_FUNCTOR_COPY tag_desc_copy
+#define HTABLE_DATA_FUNCTOR_COPY_AND_RELEASE tag_desc_copy_and_release
#include <rsys/hash_table.h>
struct scad_device {
@@ -68,7 +121,7 @@ struct scad_device {
struct scad_options options;
struct htable_names geometry_names;
struct htable_geometries allgeom;
- struct htable_tags2geom tags2geom[2];
+ struct htable_tags2desc tags2desc[2]; /* Only geoms for 2D and 3D tags for now */
int verbose;
int need_synchro;
@@ -141,21 +194,38 @@ LOCAL_SYM struct scad_device*
get_device
(void);
+LOCAL_SYM struct tag_desc*
+device_get_description
+ (const int dim,
+ const int tag);
+
+LOCAL_SYM res_T
+do_device_tags_ref_get
+ (const int* dimTags,
+ const size_t count);
+
LOCAL_SYM res_T
device_register_tags
(struct scad_geometry* geom);
LOCAL_SYM res_T
-device_unregister_tags
+do_device_tags_ref_put
(const int log,
const enum log_type log_type,
- struct scad_geometry* geom);
+ int* dimTags,
+ size_t count);
+
+LOCAL_SYM res_T
+device_register_ref_to_tags
+ (const int dim,
+ const int tag,
+ const int* dimTags,
+ const size_t count);
LOCAL_SYM res_T
-device_apply_mappings
- (const int* original_dimTags,
- int** mappings,
- size_t* mappings_counts, /* Number of items in each mapping */
- const size_t mappings_count); /* Number of mappings; count(original_dimTags)/2 */
+device_unregister_tags
+ (const int log,
+ const enum log_type log_type,
+ struct scad_geometry* geom);
#endif
diff --git a/src/scad_geometry.c b/src/scad_geometry.c
@@ -13,7 +13,6 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. */
-#include "rsys/logger.h"
#include "scad.h"
#include "scad_c.h"
#include "scad_device.h"
@@ -24,7 +23,7 @@
#include <rsys/str.h>
#include <rsys/math.h>
#include <rsys/double3.h>
-
+#include <rsys/logger.h>
#include <rsys/hash_table.h>
#include <stdlib.h>
@@ -91,10 +90,9 @@ geom_set_name
if(!same_name) {
size_t n = htable_names_erase(&dev->geometry_names, &geom->name);
ASSERT((n == 1) == !str_is_empty(&geom->name)); (void)n;
+ ERR(str_set(&geom->name, name));
}
-
if(name) {
- str_set(&geom->name, name);
ERR(htable_names_set(&dev->geometry_names, &geom->name, &geom));
} else {
str_clear(&geom->name);
@@ -134,7 +132,7 @@ mixed_dim_err_msg
}
static res_T
-scad_geometry_create
+geometry_create
(const char* name,
struct scad_geometry** out_geometry)
{
@@ -158,6 +156,17 @@ scad_geometry_create
ERR(geom_set_name(geom, name));
dev->need_synchro = 1;
+ if(dev->options.Misc.LogRefCounting & Scad_log_geometry) {
+ if(str_is_empty(&geom->name)) {
+ logger_print(dev->logger, dev->log_type,
+ "Creating unnamed geometry %p (count set to 1).\n", (void*)geom);
+ } else {
+ logger_print(dev->logger, dev->log_type,
+ "Creating geometry '%s' (%p, count set to 1).\n",
+ str_cget(&geom->name), (void*)geom);
+ }
+ }
+
end:
*out_geometry = geom;
return res;
@@ -191,6 +200,10 @@ gather_tags
/* list tags and remove duplicates */
for(i = 0; i < geometries_count; i++) {
+ if(!geometries[i]) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
for(j = 0; j < geometries[i]->gmsh_dimTags_n; j += 2) {
char one = 1;
int dim = geometries[i]->gmsh_dimTags[j];
@@ -281,9 +294,9 @@ geometry_release(ref_T* ref)
str_release(&geom->name);
n = htable_geometries_erase(&dev->allgeom, &geom);
ASSERT(n == 1); (void)n;
-#ifndef NDEBUG
- geom->gmsh_dimTags = (int*)0xdeadbeef;
-#endif
+ if(geom->release && geom->custom) {
+ (*geom->release)(geom->custom);
+ }
MEM_RM(allocator, geom);
}
@@ -294,19 +307,58 @@ res_T
scad_geometry_ref_get
(struct scad_geometry* geom)
{
+ res_T res = RES_OK;
+ struct scad_device* dev = get_device();
+
if(!geom) return RES_BAD_ARG;
+ ERR(check_device(FUNC_NAME));
+
ref_get(&geom->ref);
- return RES_OK;
+ if(dev->options.Misc.LogRefCounting & Scad_log_geometry) {
+ if(str_is_empty(&geom->name)) {
+ logger_print(dev->logger, dev->log_type,
+ "Getting a reference on unnamed geometry %p (count set to %lu).\n",
+ (void*)geom, (long unsigned)geom->ref);
+ } else {
+ logger_print(dev->logger, dev->log_type,
+ "Getting a reference on geometry '%s' (count set to %lu).\n",
+ str_cget(&geom->name), (long unsigned)geom->ref);
+ }
+ }
+
+end:
+ return res;
+error:
+ goto end;
}
res_T
scad_geometry_ref_put
(struct scad_geometry* geom)
{
+ res_T res = RES_OK;
+ struct scad_device* dev = get_device();
+
if(!geom) return RES_BAD_ARG;
+ ERR(check_device(FUNC_NAME));
+
+ if(dev->options.Misc.LogRefCounting & Scad_log_geometry) {
+ if(str_is_empty(&geom->name)) {
+ logger_print(dev->logger, dev->log_type,
+ "Putting a reference on unnamed geometry %p (count set to %lu).\n",
+ (void*)geom, (long unsigned)geom->ref - 1);
+ } else {
+ logger_print(dev->logger, dev->log_type,
+ "Putting a reference on geometry '%s' (count set to %lu).\n",
+ str_cget(&geom->name), (long unsigned)geom->ref - 1);
+ }
+ }
ref_put(&geom->ref, geometry_release);
- CHK(RES_OK == check_device(FUNC_NAME));
- return RES_OK;
+
+end:
+ return res;
+error:
+ goto end;
}
res_T
@@ -325,7 +377,7 @@ scad_scene_clear
ERR(check_device(FUNC_NAME));
allocator = dev->allocator;
- option = dev->options.Misc.LogOpenCascadeTagsRefCounting;
+ option = dev->options.Misc.LogRefCounting;
htable_geometries_init(allocator, &tmp);
ERR(htable_geometries_copy(&tmp, &dev->allgeom));
@@ -333,7 +385,8 @@ scad_scene_clear
htable_geometries_end(&tmp, &end);
empty = htable_geometries_is_empty(&dev->allgeom);
log_type = empty ? LOG_OUTPUT : LOG_WARNING;
- log = (option == Scad_log_all) || (!empty && option == Scad_log_only_undeleted);
+ log = (option & Scad_log_dimTags_all)
+ || (!empty && (option & Scad_log_dimTags_only_undeleted));
SWAP(int, dev->log, log);
SWAP(enum log_type, dev->log_type, log_type);
if(dev->log) {
@@ -388,20 +441,22 @@ error:
}
res_T
-scad_geometry_get_name
- (const struct scad_geometry* geom,
- const char** name)
+scad_geometry_set_custom_data
+ (struct scad_geometry* geom,
+ void (*release) (void* data),
+ void* data)
{
res_T res = RES_OK;
- if(!geom || !name) {
+ if(!geom) {
res = RES_BAD_ARG;
goto error;
}
ERR(check_device(FUNC_NAME));
- *name = str_cget(&geom->name);
+ geom->custom = data;
+ geom->release = release;
exit:
return res;
@@ -410,39 +465,47 @@ error:
}
res_T
-scad_geometry_swap_names
- (struct scad_geometry* geom1,
- struct scad_geometry* geom2)
+scad_geometry_get_custom_data
+ (struct scad_geometry* geom,
+ void** data)
{
res_T res = RES_OK;
- int init = 0;
- struct str tmp;
- struct scad_device* dev = get_device();
- struct mem_allocator* allocator = NULL;
- if(!geom1 || !geom2) {
+ if(!geom || !data) {
res = RES_BAD_ARG;
goto error;
}
ERR(check_device(FUNC_NAME));
- allocator = dev->allocator;
- if(!str_is_empty(&geom1->name)) {
- ERR(htable_names_set(&dev->geometry_names, &geom1->name, &geom2));
+ if(geom->release && geom->custom) {
+ (*geom->release)(geom->custom);
}
- if(!str_is_empty(&geom2->name)) {
- ERR(htable_names_set(&dev->geometry_names, &geom2->name, &geom1));
+ *data = geom->custom;
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+res_T
+scad_geometry_get_name
+ (const struct scad_geometry* geom,
+ const char** name)
+{
+ res_T res = RES_OK;
+
+ if(!geom || !name) {
+ res = RES_BAD_ARG;
+ goto error;
}
- str_init(allocator, & tmp);
- init = 1;
- ERR(str_copy(&tmp, &geom1->name));
- ERR(str_copy(&geom1->name, &geom2->name));
- ERR(str_copy(&geom2->name, &tmp));
+ ERR(check_device(FUNC_NAME));
+
+ *name = str_cget(&geom->name);
exit:
- if(init) str_release(&tmp);
return res;
error:
goto exit;
@@ -454,36 +517,31 @@ scad_geometry_get_mass
double* mass)
{
res_T res = RES_OK;
- int dim = 0;
- size_t i, count = 0;
+ int ref_dim = 0;
+ size_t i;
int* data = NULL;
size_t sz = 0;
- struct scad_device* dev = get_device();
- struct mem_allocator* allocator = NULL;
if(!geom || !mass) goto error;
ERR(check_device(FUNC_NAME));
- allocator = dev->allocator;
- ASSERT(geom->gmsh_dimTags_n % 2 == 0);
- count = geom->gmsh_dimTags_n / 2;
-
- ERR(gather_tags(&geom, 1, &data, &sz));
+ data = geom->gmsh_dimTags;
+ sz = geom->gmsh_dimTags_n;
- dim = data[0];
+ ref_dim = data[0];
*mass = 0;
- for(i=0; i<count; ++i) {
+ for(i = 0; i < sz; i += 2) {
double geom_mass = 0;
+ int dim = data[i], tag = data[i+1];
int ierr = 0;
- if(data[2*i] != dim) goto error;
- gmshModelOccGetMass(data[2*i], data[2*i + 1], &geom_mass, &ierr);
+ if(ref_dim != dim) goto error;
+ gmshModelOccGetMass(dim, tag, &geom_mass, &ierr);
ERR(gmsh_err_to_res_T(ierr));
*mass += geom_mass;
}
exit:
- if(allocator) MEM_RM(allocator, data);
return res;
error:
goto exit;
@@ -546,7 +604,7 @@ scad_add_rectangle
gmsh_ID = gmshModelOccAddRectangle(SPLIT3(xyz), SPLIT2(dxdy), -1, 0, &ierr);
ERR(gmsh_err_to_res_T(ierr));
- ERR(scad_geometry_create(name, &geom));
+ ERR(geometry_create(name, &geom));
geom->gmsh_dimTags_n = 2;
geom->gmsh_dimTags
= MEM_ALLOC(allocator, geom->gmsh_dimTags_n * sizeof(*geom->gmsh_dimTags));
@@ -594,7 +652,7 @@ scad_add_disk
gmsh_ID = gmshModelOccAddDisk(SPLIT3(xyz), radius, radius, -1, &ierr);
ERR(gmsh_err_to_res_T(ierr));
- ERR(scad_geometry_create(name, &geom));
+ ERR(geometry_create(name, &geom));
geom->gmsh_dimTags_n = 2;
geom->gmsh_dimTags
= MEM_ALLOC(allocator, geom->gmsh_dimTags_n * sizeof(*geom->gmsh_dimTags));
@@ -671,7 +729,7 @@ scad_add_polygon
gmsh_ID = gmshModelOccAddPlaneSurface(&loop, 1, -1, &ierr);
ERR(gmsh_err_to_res_T(ierr));
- ERR(scad_geometry_create(name, &geom));
+ ERR(geometry_create(name, &geom));
geom->gmsh_dimTags_n = 2;
geom->gmsh_dimTags = MEM_ALLOC(allocator, 2 * sizeof(*geom->gmsh_dimTags));
if(!geom->gmsh_dimTags) {
@@ -722,7 +780,7 @@ scad_add_box
gmsh_ID = gmshModelOccAddBox(SPLIT3(xyz), SPLIT3(dxdydz), -1, &ierr);
ERR(gmsh_err_to_res_T(ierr));
- ERR(scad_geometry_create(name, &geom));
+ ERR(geometry_create(name, &geom));
geom->gmsh_dimTags_n = 2;
geom->gmsh_dimTags = MEM_ALLOC(allocator, 2 * sizeof(*geom->gmsh_dimTags));
if(!geom->gmsh_dimTags) {
@@ -772,7 +830,7 @@ scad_add_cylinder
angle, &ierr);
ERR(gmsh_err_to_res_T(ierr));
- ERR(scad_geometry_create(name, &geom));
+ ERR(geometry_create(name, &geom));
geom->gmsh_dimTags_n = 2;
geom->gmsh_dimTags = MEM_ALLOC(allocator, 2 * sizeof(*geom->gmsh_dimTags));
if(!geom->gmsh_dimTags) {
@@ -820,7 +878,7 @@ scad_add_sphere
gmshModelOccAddSphere(SPLIT3(xyz), radius, -1, -PI/2, PI/2, 2*PI, &ierr);
ERR(gmsh_err_to_res_T(ierr));
- ERR(scad_geometry_create(name, &geom));
+ ERR(geometry_create(name, &geom));
geom->gmsh_dimTags_n = 2;
geom->gmsh_dimTags = MEM_ALLOC(allocator, 2 * sizeof(*geom->gmsh_dimTags));
if(!geom->gmsh_dimTags) {
@@ -843,6 +901,144 @@ error:
goto exit;
}
+SCAD_API res_T
+scad_geometries_equal
+ (struct scad_geometry* geom1,
+ struct scad_geometry* geom2,
+ int* equal)
+{
+ res_T res = RES_OK;
+ struct scad_device* dev = get_device();
+ struct mem_allocator* allocator = NULL;
+ struct htable_tags t2, t3;
+ int eq = 1, initialized = 0;
+
+ if(!geom1 || !geom2 || !equal) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ ERR(check_device(FUNC_NAME));
+ allocator = dev->allocator;
+
+ /* Trivial cases */
+ if(geom1 == geom2) {
+ eq = 1;
+ }
+ else if(geom1->gmsh_dimTags_n != geom2->gmsh_dimTags_n) {
+ eq = 0;
+ } else {
+ size_t i;
+ htable_tags_init(allocator, &t2);
+ htable_tags_init(allocator, &t3);
+ initialized = 1;
+ /* Create tables from geom1 tags */
+ for(i = 0; i < geom1->gmsh_dimTags_n; i += 2) {
+ char one = 1;
+ int d = geom1->gmsh_dimTags[i];
+ int t = geom1->gmsh_dimTags[i+1];
+ struct htable_tags* tn = (d == 2) ? &t2 : &t3;
+ ERR(htable_tags_set(tn, &t, &one));
+ }
+ ASSERT((htable_tags_size_get(&t2) + htable_tags_size_get(&t3)) * 2
+ == geom1->gmsh_dimTags_n);
+ /* Check if tags from geom2 are included */
+ for(i = 0; i < geom2->gmsh_dimTags_n; i += 2) {
+ char* found;
+ int d = geom2->gmsh_dimTags[i];
+ int t = geom2->gmsh_dimTags[i+1];
+ struct htable_tags* tn = (d == 2) ? &t2 : &t3;
+ found = htable_tags_find(tn, &t);
+ if(!found) {
+ eq = 0;
+ break;
+ }
+ }
+ }
+
+ *equal = eq;
+
+exit:
+ if(initialized) {
+ htable_tags_release(&t2);
+ htable_tags_release(&t3);
+ }
+ return res;
+error:
+ goto exit;
+}
+
+SCAD_API res_T
+scad_geometry_is_included
+ (struct scad_geometry* geometry,
+ struct scad_geometry** geometries,
+ const size_t geometries_count,
+ int* included)
+{
+ res_T res = RES_OK;
+ struct scad_device* dev = get_device();
+ struct mem_allocator* allocator = NULL;
+ struct htable_tags t2, t3;
+ int initialized = 0;
+ size_t i, n;
+
+ if(!geometry || !geometries || !included) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ ERR(check_device(FUNC_NAME));
+ allocator = dev->allocator;
+
+ /* Trivial case */
+ for(i = 0; i < geometries_count; i++) {
+ if(geometry == geometries[i]) {
+ *included = 1;
+ goto exit;
+ }
+ }
+
+ /* Create tables from geometries tags */
+ htable_tags_init(allocator, &t2);
+ htable_tags_init(allocator, &t3);
+ initialized = 1;
+ for(n = 0; n < geometries_count; n++) {
+ const struct scad_geometry* geom = geometries[n];
+ for(i = 0; i < geometries[n]->gmsh_dimTags_n; i += 2) {
+ char one = 1;
+ int d = geom->gmsh_dimTags[i];
+ int t = geom->gmsh_dimTags[i+1];
+ struct htable_tags* tn = (d == 2) ? &t2 : &t3;
+ ERR(htable_tags_set(tn, &t, &one));
+ }
+ }
+
+ /* Check if tags from geometry are included */
+ for(i = 0; i < geometry->gmsh_dimTags_n; i += 2) {
+ char* found;
+ int d = geometry->gmsh_dimTags[i];
+ int t = geometry->gmsh_dimTags[i+1];
+ struct htable_tags* tn = (d == 2) ? &t2 : &t3;
+ found = htable_tags_find(tn, &t);
+ if(!found) {
+ *included = 0;
+ goto exit;
+ }
+ }
+
+ /* If here, no not-included tag was found */
+ *included = 1;
+
+exit:
+ if(initialized) {
+ htable_tags_release(&t2);
+ htable_tags_release(&t3);
+ }
+ return res;
+error:
+ goto exit;
+}
+
res_T
scad_fuse_geometries
(const char* name,
@@ -876,12 +1072,12 @@ scad_fuse_geometries
ERR(gather_tags(tools, tools_count, &data2, &sz2));
/* We don't remove gmsh objects here; they are only removed when their tags are
- * no longuer used by any star-cad geometry */
+ * no longer used by any star-cad geometry */
gmshModelOccFuse(data1, sz1, data2, sz2, &tagout, &tagoutn, &map, &mapn,
&mapnn, -1, 0, 0, &ierr);
ERR(gmsh_err_to_res_T(ierr));
- ERR(scad_geometry_create(name, &geom));
+ ERR(geometry_create(name, &geom));
geom->gmsh_dimTags_n = tagoutn;
geom->gmsh_dimTags = MEM_ALLOC(allocator, tagoutn * sizeof(*tagout));
if(!geom->gmsh_dimTags) {
@@ -912,6 +1108,44 @@ error:
}
res_T
+scad_collect_geometries
+ (const char* name,
+ struct scad_geometry** geometries,
+ const size_t geometries_count,
+ struct scad_geometry** out_geometry)
+{
+ res_T res = RES_OK;
+ size_t sz;
+ int* data = NULL;
+ struct scad_geometry* geom = NULL;
+
+ if(!geometries || !geometries_count || !out_geometry) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ ERR(check_device(FUNC_NAME));
+
+ ERR(gather_tags(geometries, geometries_count, &data, &sz));
+
+ ERR(geometry_create(name, &geom));
+ geom->gmsh_dimTags_n = sz;
+ geom->gmsh_dimTags = data;
+
+ ERR(device_register_tags(geom));
+
+exit:
+ if(out_geometry) *out_geometry = geom;
+ return res;
+error:
+ if(geom) {
+ SCAD(geometry_ref_put(geom));
+ geom = NULL;
+ }
+ goto exit;
+}
+
+res_T
scad_cut_geometries
(const char* name, /* Can be NULL */
struct scad_geometry** geometries,
@@ -944,12 +1178,12 @@ scad_cut_geometries
ERR(gather_tags(tools, tools_count, &data2, &sz2));
/* We don't remove gmsh objects here; they are only removed when their tags are
- * no longuer used by any star-cad geometry */
+ * no longer used by any star-cad geometry */
gmshModelOccCut(data1, sz1, data2, sz2, &tagout, &tagoutn, &map, &mapn,
&mapnn, -1, 0, 0, &ierr);
ERR(gmsh_err_to_res_T(ierr));
- ERR(scad_geometry_create(name, &geom));
+ ERR(geometry_create(name, &geom));
geom->gmsh_dimTags_n = tagoutn;
geom->gmsh_dimTags = MEM_ALLOC(allocator, tagoutn * sizeof(*tagout));
if(!geom->gmsh_dimTags) {
@@ -1017,12 +1251,12 @@ scad_intersect_geometries
ERR(gather_tags(tools, tools_count, &data2, &sz2));
/* We don't remove gmsh objects here; they are only removed when their tags are
- * no longuer used by any star-cad geometry */
+ * no longer used by any star-cad geometry */
gmshModelOccIntersect(data1, sz1, data2, sz2, &tagout, &tagoutn, &map, &mapn,
&mapnn, -1, 0, 0, &ierr);
ERR(gmsh_err_to_res_T(ierr));
- ERR(scad_geometry_create(name, &geom));
+ ERR(geometry_create(name, &geom));
geom->gmsh_dimTags_n = tagoutn;
if (tagoutn == 0){
geom->gmsh_dimTags = NULL;
@@ -1079,6 +1313,11 @@ scad_geometries_common_boundaries
struct scad_geometry* geom = NULL;
struct mem_allocator* allocator = NULL;
struct scad_device* dev = get_device();
+ int log = (dev->options.Misc.LogRefCounting & Scad_log_dimTags_all);
+ enum log_type log_type = dev->log_type;
+ size_t i;
+ struct str msg;
+ int init = 0;
if(!geometries || !geometries_count || !tools || !tools_count || !out_geometry) {
res = RES_BAD_ARG;
@@ -1092,7 +1331,7 @@ scad_geometries_common_boundaries
ERR(gather_tags(tools, tools_count, &data2, &sz2));
/* We don't remove gmsh objects here; they are only removed when their tags are
- * no longuer used by any star-cad geometry */
+ * no longer used by any star-cad geometry */
gmshModelGetBoundary(data1, sz1, &bound1, &n1, 1, 0, 0, &ierr);
ERR(gmsh_err_to_res_T(ierr));
gmshModelGetBoundary(data2, sz2, &bound2, &n2, 1, 0, 0, &ierr);
@@ -1101,7 +1340,7 @@ scad_geometries_common_boundaries
&mapn, &mapnn, -1, 0/*no delete*/, 0/*no delete*/, &ierr);
ERR(gmsh_err_to_res_T(ierr));
- ERR(scad_geometry_create(name, &geom));
+ ERR(geometry_create(name, &geom));
geom->gmsh_dimTags_n = tagoutn;
if (tagoutn == 0) {
geom->gmsh_dimTags = NULL;
@@ -1116,7 +1355,51 @@ scad_geometries_common_boundaries
ERR(device_register_tags(geom));
+ if(log) {
+ str_init(allocator, &msg);
+ init = 1;
+ logger_print(dev->logger, log_type,
+ "Common boundaries specific tag management:\n");
+ ERR(str_printf(&msg, " tags ["));
+ for(i = 0; i < tagoutn; i += 2) {
+ const int dim = tagout[i];
+ const int tag = tagout[i+1];
+ ERR(str_append_printf(&msg, (i ? ", %d.%d" : "%d.%d"), dim, tag));
+ }
+ ERR(str_append_printf(&msg, "] getting a ref to tags ["));
+ for(i = 0; i < sz1; i += 2) {
+ const int dim = data1[i];
+ const int tag = data1[i+1];
+ ERR(str_append_printf(&msg, (i ? ", %d.%d" : "%d.%d"), dim, tag));
+ }
+ for(i = 0; i < sz2; i += 2) {
+ const int dim = data2[i];
+ const int tag = data2[i+1];
+ ERR(str_append_printf(&msg, ", %d.%d", dim, tag));
+ }
+ logger_print(dev->logger, log_type, "%s].\n", str_cget(&msg));
+ }
+ for(i = 0; i < tagoutn; i += 2) {
+ int dim = tagout[i];
+ int tag = tagout[i+1];
+ struct tag_desc* desc = device_get_description(dim, tag);
+ ASSERT(dim == 2);
+ if(!desc) {
+ res = RES_BAD_OP;
+ goto error;
+ }
+ /* Need to protect out_geometry's tags by getting a ref on input tags or
+ * deleting input geometry will possibly delete them */
+ ERR(device_register_ref_to_tags(dim, tag, data1, sz1));
+ ERR(device_register_ref_to_tags(dim, tag, data2, sz2));
+ /* As the 2D tags will be deleted when the 3D tag they are part of are
+ * deleted, they shouldn't be deleted when the geometry they belongs to are
+ * released. */
+ desc->delete_policy = Scad_do_not_delete;
+ }
+
exit:
+ if(init) str_release(&msg);
if(out_geometry) *out_geometry = geom;
if(allocator) {
MEM_RM(allocator, data1);
@@ -1148,52 +1431,64 @@ error:
res_T
scad_geometry_rotate
- (struct scad_geometry* geom,
+ (const struct scad_geometry* geom,
const double pt[3],
const double axis[3],
- const double angle)
+ const double angle,
+ const char* name, /* Can be NULL */
+ struct scad_geometry** out_geometry)
{
- int* data;
- size_t sz;
int ierr = 0;
+ struct scad_geometry* out = NULL;
res_T res = RES_OK;
- if(!geom || !pt || !axis) {
+ if(!geom || !pt || !axis || ! out_geometry) {
res = RES_BAD_ARG;
goto error;
}
ERR(check_device(FUNC_NAME));
- sz = geom->gmsh_dimTags_n;
- data = geom->gmsh_dimTags;
- gmshModelOccRotate(data, sz, SPLIT3(pt), SPLIT3(axis), angle, &ierr);
+ ERR(scad_geometry_copy(geom, name, &out));
+
+ gmshModelOccRotate(out->gmsh_dimTags, out->gmsh_dimTags_n, SPLIT3(pt),
+ SPLIT3(axis), angle, &ierr);
get_device()->need_synchro = 1;
ERR(gmsh_err_to_res_T(ierr));
exit:
+ if(out_geometry) *out_geometry = out;
return res;
error:
+ if(out) SCAD(geometry_ref_put(out));
+ out = NULL;
goto exit;
}
res_T
scad_geometry_extrude
- (const struct scad_geometry* geom,
+ (struct scad_geometry* geom,
const char* name,
const double dxdydz[3],
struct scad_geometry** out_geometry)
{
res_T res = RES_OK;
- int* tagout = NULL;
+ int *tagout = NULL;
size_t tagoutn;
- size_t i, j;
- int* extrude_data = NULL;
+ size_t i;
+#ifndef NDEBUG
+ size_t j;
+#endif
+ int *ed, *extrude_data = NULL;
size_t extrude_sz = 0;
int ierr = 0;
struct scad_geometry* extrude_geom = NULL;
struct scad_device* dev = get_device();
+ int log = (dev->options.Misc.LogRefCounting & Scad_log_dimTags_all);
+ enum log_type log_type = dev->log_type;
struct mem_allocator* allocator = NULL;
+ struct str msg;
+ int init = 0;
if(!geom || !dxdydz || !out_geometry) {
res = RES_BAD_ARG;
@@ -1205,13 +1500,12 @@ scad_geometry_extrude
gmshModelOccExtrude(geom->gmsh_dimTags, geom->gmsh_dimTags_n, SPLIT3(dxdydz),
&tagout, &tagoutn, NULL, 0, NULL, 0, 0, &ierr);
- get_device()->need_synchro = 1;
ERR(gmsh_err_to_res_T(ierr));
+ get_device()->need_synchro = 1;
- ERR(scad_geometry_create(name, &extrude_geom));
- /* keep only 3D entities */
- /* TODO : NOT SURE OF THE CONCEPT */
- for(i=0; i<tagoutn; i+=2) {
+ /* Output includes both the 3D result and its 2D constituants.
+ * Keep only 3D entities. */
+ for(i = 0; i < tagoutn; i += 2) {
int dim = tagout[i];
if(dim == 3) extrude_sz += 2;
}
@@ -1220,22 +1514,66 @@ scad_geometry_extrude
res = RES_MEM_ERR;
goto error;
}
- j = 0;
- for(i=0; i<tagoutn; i+=2) {
+ ed = extrude_data;
+ for(i = 0; i < tagoutn; i += 2) {
int dim = tagout[i];
int tag = tagout[i+1];
+#ifndef NDEBUG
+ /* Expecting geom's tags not part of tagout */
+ for(j = 0; j < geom->gmsh_dimTags_n; j += 2) {
+ ASSERT(dim != geom->gmsh_dimTags[j] || tag != geom->gmsh_dimTags[j+1]);
+ }
+#endif
if(dim == 3) {
- extrude_data[j] = dim;
- extrude_data[j+1] = tag;
- j += 2;
+ *ed++ = dim;
+ *ed++ = tag;
}
}
- ASSERT(j == extrude_sz);
+
+ ERR(geometry_create(name, &extrude_geom));
extrude_geom->gmsh_dimTags_n = extrude_sz;
extrude_geom->gmsh_dimTags = extrude_data;
+
ERR(device_register_tags(extrude_geom));
+ if(log) {
+ str_init(allocator, &msg);
+ init = 1;
+ logger_print(dev->logger, log_type, "Extrude specific tag management:\n");
+ ERR(str_printf(&msg, " tags ["));
+ for(i = 0; i < geom->gmsh_dimTags_n; i += 2) {
+ const int dim = geom->gmsh_dimTags[i];
+ const int tag = geom->gmsh_dimTags[i+1];
+ ERR(str_append_printf(&msg, (i ? ", %d.%d" : "%d.%d"), dim, tag));
+ }
+ ERR(str_append_printf(&msg, "] getting a ref to tags ["));
+ for(i = 0; i < extrude_sz; i += 2) {
+ const int dim = extrude_data[i];
+ const int tag = extrude_data[i+1];
+ ERR(str_append_printf(&msg, (i ? ", %d.%d" : "%d.%d"), dim, tag));
+ }
+ logger_print(dev->logger, log_type, "%s].\n", str_cget(&msg));
+ }
+ for(i = 0; i < geom->gmsh_dimTags_n; i += 2) {
+ const int dim = geom->gmsh_dimTags[i];
+ const int tag = geom->gmsh_dimTags[i+1];
+ struct tag_desc* desc = device_get_description(dim, tag);
+ ASSERT(dim == 2);
+ if(!desc) {
+ res = RES_BAD_OP;
+ goto error;
+ }
+ /* Need to protect input geometry's tags by getting a ref on output tags or
+ * deleting out_geometry will possibly delete them */
+ ERR(device_register_ref_to_tags(dim, tag, extrude_data, extrude_sz));
+ /* As the 2D tags will be deleted when the 3D tag they are part of are
+ * deleted, they shouldn't be deleted when the geometry they belongs to are
+ * released. */
+ desc->delete_policy = Scad_do_not_delete;
+ }
+
exit:
+ if(init) str_release(&msg);
if(out_geometry) *out_geometry = extrude_geom;
gmshFree(tagout);
return res;
@@ -1244,6 +1582,7 @@ error:
SCAD(geometry_ref_put(extrude_geom));
extrude_geom = NULL;
}
+ if(tagout) gmshModelOccRemove(tagout, tagoutn, 1, &ierr);
goto exit;
}
@@ -1282,18 +1621,19 @@ scad_geometry_explode
goto error;
}
- base_name = prefix_name ? prefix_name : str_cget(&geom->name);
- if(base_name) {
+ if(prefix_name || !str_is_empty(&geom->name)) {
+ base_name = prefix_name ? prefix_name : str_cget(&geom->name);
str_init(allocator, &name);
name_initialized = 1;
}
- for(i=0; i<sz/2; ++i) {
+
+ for(i = 0; i < sz/2; ++i) {
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));
+ ERR(geometry_create(str_cget(&name), geom_array+i));
} else {
- ERR(scad_geometry_create(NULL, geom_array+i));
+ ERR(geometry_create(NULL, geom_array+i));
}
geom_array[i]->gmsh_dimTags_n = 2;
geom_array[i]->gmsh_dimTags
@@ -1354,7 +1694,7 @@ scad_geometry_copy
get_device()->need_synchro = 1;
ERR(gmsh_err_to_res_T(ierr));
- ERR(scad_geometry_create(name, ©));
+ ERR(geometry_create(name, ©));
copy->gmsh_dimTags_n = tagoutn;
copy->gmsh_dimTags = MEM_ALLOC(allocator, tagoutn * sizeof(*tagout));
if(!copy->gmsh_dimTags) {
@@ -1401,55 +1741,53 @@ error:
}
res_T
-scad_geometry_reverse
- (struct scad_geometry* geom)
+scad_geometry_translate
+ (const struct scad_geometry* geom,
+ const double dxdydz[3],
+ const char* name, /* Can be NULL */
+ struct scad_geometry** out_geometry)
{
int ierr = 0;
+ struct scad_geometry* out = NULL;
res_T res = RES_OK;
- if(!geom) {
+ if(!geom || !dxdydz || ! out_geometry) {
res = RES_BAD_ARG;
goto error;
}
ERR(check_device(FUNC_NAME));
- gmshModelMeshReverse(geom->gmsh_dimTags, geom->gmsh_dimTags_n, &ierr);
+ ERR(scad_geometry_copy(geom, name, &out));
+
+ gmshModelOccTranslate(out->gmsh_dimTags, out->gmsh_dimTags_n, SPLIT3(dxdydz),
+ &ierr);
+ get_device()->need_synchro = 1;
ERR(gmsh_err_to_res_T(ierr));
exit:
+ if(out_geometry) *out_geometry = out;
return res;
error:
+ if(out) SCAD(geometry_ref_put(out));
+ out = NULL;
goto exit;
}
-res_T
-scad_geometry_translate
- (struct scad_geometry* geom,
- const double dxdydz[3])
+static char
+geom_uses_dimTag
+ (const struct scad_geometry* g,
+ const int dim,
+ const int tag)
{
- int* data;
- size_t sz;
- int ierr = 0;
- res_T res = RES_OK;
-
- if(!geom || !dxdydz) {
- res = RES_BAD_ARG;
- goto error;
+ size_t i;
+ ASSERT(g);
+ for(i = 0; i < g->gmsh_dimTags_n; i+=2) {
+ int d = g->gmsh_dimTags[i];
+ int t = g->gmsh_dimTags[i+1];
+ if(dim == d && tag == t) return 1;
}
-
- ERR(check_device(FUNC_NAME));
-
- sz = geom->gmsh_dimTags_n;
- data = geom->gmsh_dimTags;
- gmshModelOccTranslate(data, sz, SPLIT3(dxdydz), &ierr);
- get_device()->need_synchro = 1;
- ERR(gmsh_err_to_res_T(ierr));
-
-exit:
- return res;
-error:
- goto exit;
+ return 0;
}
res_T
@@ -1469,14 +1807,15 @@ scad_geometries_partition
int ierr = 0;
struct scad_geometry** geoms = NULL;
struct htable_mappings m2, m3;
- int hm_initialized = 0;
struct scad_device* dev = get_device();
struct htable_tags t2, t3;
struct htable_tags_iterator it, end;
int ht_initialized = 0;
struct mem_allocator* allocator = NULL;
+ int dont_call_fragment = 0;
+ char* overlap = NULL;
- if(!geometries || !geometries_count || (allow_overlapping && !out_geometries)) {
+ if(!geometries || !geometries_count || !out_geometries) {
res = RES_BAD_ARG;
goto error;
}
@@ -1486,136 +1825,171 @@ scad_geometries_partition
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_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. */
- gmshModelOccFragment(data, sz, NULL, 0, &tagout, &tagoutn, &map, &mapn,
- &mapnn, -1, (allow_overlapping == 0), 0, &ierr);
- ERR(gmsh_err_to_res_T(ierr));
- ASSERT(sz == 2*mapnn); /* Because input tags where deduplicated */
+ /* Create output geometries */
+ geoms = MEM_CALLOC(allocator, geometries_count, sizeof(*geoms));
+ if(!geoms) {
+ res = RES_MEM_ERR;
+ goto error;
+ }
- get_device()->need_synchro = 1;
+ dont_call_fragment = (sz == 2);
+ if(dont_call_fragment) {
+ /* gmshModelOccFragment doesn't allow to process a single entity: need to
+ * create the result (same as input) here.
+ * Not so simple as one could have provided more than 1 input geometry with
+ * identical or no dim.tag(s). */
+ mapnn = 1;
+ map = gmshMalloc(sizeof(*map));
+ map[0] = gmshMalloc(2 * sizeof(**map));
+ mapn = gmshMalloc(sizeof(*mapn));
+ if(!map || !map[0] || !mapn) {
+ res = RES_MEM_ERR;
+ goto error;
+ }
+ mapn[0] = 2;
+ map[0][0] = data[0];
+ map[0][1] = data[1];
+ } else {
+ /* As a general principle, we don't remove gmsh objects directly; they are
+ * only removed from scad_geometry_ref_put when their tags are no longer
+ * used by any star-cad geometry. */
+ gmshModelOccFragment(data, sz, NULL, 0, &tagout, &tagoutn, &map, &mapn,
+ &mapnn, -1, 0, 0, &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+ ASSERT(sz == 2*mapnn); /* Because input tags where deduplicated */
- /* Check first if there was an overlapping problem */
- if(!allow_overlapping) {
- /* No overlapping means that each tag in geometries is translated into a
- * single tag in map */
- unsigned long ucount = 0;
- for(i = 0; i < mapnn; i++) {
- if(mapn[i] != 2) {
- res = RES_BAD_ARG;
- if(str_is_empty(&geometries[i]->name)) {
- ucount++;
- } else {
- log_error(get_device(), "Geometry '%s' overlapping.\n",
- str_cget(&geometries[i]->name));
+ get_device()->need_synchro = 1;
+
+ /* Check first if there was an overlapping problem */
+ if(!allow_overlapping) {
+ /* No overlapping means that each tag in geometries is translated into a
+ * single tag in map */
+ size_t ov = 0;
+ overlap = MEM_CALLOC(allocator, geometries_count, sizeof(*overlap));
+ if(!overlap) {
+ res = RES_MEM_ERR;
+ goto error;
+ }
+ for(i = 0; i < mapnn; i++) {
+ if(mapn[i] != 2) {
+ int dim = data[2*i];
+ int tag = data[2*i+1];
+ size_t k;
+ ov++;
+ res = RES_BAD_ARG;
+ /* dim.tag #i overlaps: search geometries using it in input */
+ for(k = 0; k < geometries_count; k++) {
+ overlap[k] |= geom_uses_dimTag(geometries[k], dim, tag);
+ }
}
}
- /* Additionally, with the delete flag ON we expect the tags to remain
- * unchanged in the non-overlapping case. */
- ASSERT(res != RES_OK || allow_overlapping || map[i][1] == data[2*i+1]);
- }
- if(ucount) {
- log_error(get_device(), "%lu unamed overlapping geometries.\n", ucount);
+ if(ov) {
+ size_t k;
+ for(k = 0; k < geometries_count; k++) {
+ struct scad_geometry* g = geometries[k];
+ if(!overlap[k]) continue;
+ if(str_is_empty(&g->name)) {
+ log_error(get_device(), "Unnamed geometry '%p' overlapping.\n",
+ (void*)g);
+ } else {
+ log_error(get_device(), "Geometry '%s' overlapping.\n",
+ str_cget(&g->name));
+ }
+ }
+ }
+ if(ov) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
}
- if(res != RES_OK) goto error;
- } else {
- /* Create htables of mappings to ease access */
- htable_mappings_init(allocator, &m2);
- htable_mappings_init(allocator, &m3);
- hm_initialized = 1;
- for(i = 0; i < sz; i += 2) {
- int dim = data[i];
- int tag = data[i+1];
- size_t mapping = i/2;
+ }
+
+ /* Create htables (mappings are used to ease access) */
+ htable_mappings_init(allocator, &m2);
+ htable_mappings_init(allocator, &m3);
+ htable_tags_init(allocator, &t2);
+ htable_tags_init(allocator, &t3);
+ ht_initialized = 1;
+ for(i = 0; i < sz; i += 2) {
+ int dim = data[i];
+ int tag = data[i+1];
+ size_t mapping = i/2;
+ struct htable_mappings* mn = (dim == 2) ? &m2 : &m3;
+ ASSERT(dim == 2 || dim == 3);
+ ERR(htable_mappings_set(mn, &tag, &mapping));
+ }
+
+ for(i = 0; i < geometries_count; i++) {
+ struct scad_geometry* geom = geometries[i];
+ size_t c, n, j;
+ int* dt = NULL;
+ /* For each tag in geometries[i] out_geometries[i] includes the mapped tags.
+ * Because of the overlapping case, the resulting tags need to be
+ * deduplicated though. */
+ htable_tags_clear(&t2);
+ htable_tags_clear(&t3);
+ for(j = 0; j < geom->gmsh_dimTags_n; j += 2) {
+ int dim = geom->gmsh_dimTags[j];
+ int tag = geom->gmsh_dimTags[j+1];
struct htable_mappings* mn = (dim == 2) ? &m2 : &m3;
+ size_t k;
+ size_t* mapping = htable_mappings_find(mn, &tag);
ASSERT(dim == 2 || dim == 3);
- ERR(htable_mappings_set(mn, &tag, &mapping));
+ ASSERT(mapping && *mapping < mapnn);
+ for(k = 0; k < mapn[*mapping]; k += 2) {
+ char one = 1;
+ int d = map[*mapping][k];
+ int t = map[*mapping][k+1];
+ struct htable_tags* tn = (d == 2) ? &t2 : &t3;
+ ERR(htable_tags_set(tn, &t, &one));
+ }
}
-
- /* Create output geometries from mapping */
- geoms = MEM_CALLOC(allocator, geometries_count, sizeof(*geoms));
- if(!geoms) {
+ /* Allocate result */
+ n = htable_tags_size_get(&t2) + htable_tags_size_get(&t3);
+ dt = MEM_ALLOC(allocator, sizeof(*dt) * 2 * n);
+ if(!dt) {
res = RES_MEM_ERR;
goto error;
}
- htable_tags_init(allocator, &t2);
- htable_tags_init(allocator, &t3);
- ht_initialized = 1;
- for(i = 0; i < geometries_count; i++) {
- struct scad_geometry* geom = geometries[i];
- size_t c, n, j;
- int* dt = NULL;
- /* For each tag in geometries[i] out_geometries[i] includes the mapped tags.
- * The resulting tags need to be deduplicated though. */
- htable_tags_clear(&t2);
- htable_tags_clear(&t3);
- for(j = 0; j < geom->gmsh_dimTags_n; j += 2) {
- int dim = geom->gmsh_dimTags[j];
- int tag = geom->gmsh_dimTags[j+1];
- struct htable_mappings* mn = (dim == 2) ? &m2 : &m3;
- size_t k;
- size_t* mapping = htable_mappings_find(mn, &tag);
- ASSERT(dim == 2 || dim == 3);
- ASSERT(mapping && *mapping < mapnn);
- for(k = 0; k < mapn[*mapping]; k += 2) {
- char one = 1;
- int d = map[*mapping][k];
- int t = map[*mapping][k+1];
- struct htable_tags* tn = (d == 2) ? &t2 : &t3;
- ERR(htable_tags_set(tn, &t, &one));
- }
- }
- /* Allocate result */
- n = htable_tags_size_get(&t2) + htable_tags_size_get(&t3);
- dt = MEM_ALLOC(allocator, sizeof(*dt) * 2 * n);
- if(!dt) {
- res = RES_MEM_ERR;
- goto error;
- }
- /* Copy tags */
- c = 0;
- htable_tags_begin(&t2, &it);
- htable_tags_end(&t2, &end);
- while(!htable_tags_iterator_eq(&it, &end)) {
- dt[c++] = 2;
- dt[c++] = *htable_tags_iterator_key_get(&it);
- htable_tags_iterator_next(&it);
- }
- htable_tags_begin(&t3, &it);
- htable_tags_end(&t3, &end);
- while(!htable_tags_iterator_eq(&it, &end)) {
- dt[c++] = 3;
- dt[c++] = *htable_tags_iterator_key_get(&it);
- htable_tags_iterator_next(&it);
- }
- ASSERT(c == 2*n);
-
- /* Create geometry */
- ERR(scad_geometry_create(NULL, geoms+i));
- geoms[i]->gmsh_dimTags_n = c;
- geoms[i]->gmsh_dimTags = dt;
- ERR(device_register_tags(geoms[i]));
+ /* Copy tags */
+ c = 0;
+ htable_tags_begin(&t2, &it);
+ htable_tags_end(&t2, &end);
+ while(!htable_tags_iterator_eq(&it, &end)) {
+ dt[c++] = 2;
+ dt[c++] = *htable_tags_iterator_key_get(&it);
+ htable_tags_iterator_next(&it);
+ }
+ htable_tags_begin(&t3, &it);
+ htable_tags_end(&t3, &end);
+ while(!htable_tags_iterator_eq(&it, &end)) {
+ dt[c++] = 3;
+ dt[c++] = *htable_tags_iterator_key_get(&it);
+ htable_tags_iterator_next(&it);
}
- memcpy(out_geometries, geoms, geometries_count * sizeof(*geoms));
+ ASSERT(c == 2*n);
+
+ /* Create geometry */
+ ERR(geometry_create(NULL, geoms+i));
+ geoms[i]->gmsh_dimTags_n = c;
+ geoms[i]->gmsh_dimTags = dt;
+ ERR(device_register_tags(geoms[i]));
}
+ memcpy(out_geometries, geoms, geometries_count * sizeof(*geoms));
exit:
gmshFree(mapn);
free_gmsh_map(map, mapnn);
- if(hm_initialized) {
+ if(ht_initialized) {
htable_mappings_release(&m2);
htable_mappings_release(&m3);
- }
- if(ht_initialized) {
htable_tags_release(&t2);
htable_tags_release(&t3);
}
if(allocator) {
MEM_RM(allocator, data);
MEM_RM(allocator, geoms);
+ MEM_RM(allocator, overlap);
}
gmshFree(tagout);
return res;
@@ -1634,70 +2008,135 @@ error:
}
res_T
-scad_fragment_geometries
- (const char* name,
- struct scad_geometry** geometries,
- const size_t geometries_count,
- struct scad_geometry** tools,
- const size_t tools_count,
- struct scad_geometry** out_geometry)
+scad_geometries_swap
+ (struct scad_geometry** pool1,
+ struct scad_geometry** pool2,
+ const size_t count,
+ const int flags)
{
res_T res = RES_OK;
- int* tagout = NULL;
- int** map = NULL;
- size_t* mapn = NULL;
- size_t tagoutn, mapnn = 0, sz1, sz2;
- int* data1 = NULL;
- int* data2 = NULL;
- int ierr = 0;
- struct scad_geometry* geom = NULL;
struct scad_device* dev = get_device();
+ size_t i;
struct mem_allocator* allocator = NULL;
+ struct str tmp, msg;
- if(!geometries || !geometries_count || !tools || !tools_count || !out_geometry) {
+ if(!pool1 || !pool2) {
res = RES_BAD_ARG;
goto error;
}
- ERR(check_device(FUNC_NAME));
allocator = dev->allocator;
-
- ERR(gather_tags(geometries, geometries_count, &data1, &sz1));
- ERR(gather_tags(tools, tools_count, &data2, &sz2));
-
- /* We don't remove gmsh objects here; they are only removed when their tags are
- * no longuer used by any star-cad geometry */
- gmshModelOccFragment(data1, sz1, data2, sz2, &tagout, &tagoutn, &map, &mapn,
- &mapnn, -1, 0, 0, &ierr);
- ERR(gmsh_err_to_res_T(ierr));
-
- ERR(scad_geometry_create(name, &geom));
- geom->gmsh_dimTags_n = tagoutn;
- geom->gmsh_dimTags = MEM_ALLOC(allocator, tagoutn * sizeof(*tagout));
- if(!geom->gmsh_dimTags) {
- res = RES_MEM_ERR;
- goto error;
+ if(flags & Scad_swap_name) str_init(allocator, &tmp);
+ if(flags & Scad_swap_geometry && dev->log) str_init(allocator, &msg);
+ for(i = 0; i < count; i++) {
+ struct scad_geometry *g1 = pool1[i], *g2 = pool2[i];
+ size_t c1 = g1->gmsh_dimTags_n, c2 = g2->gmsh_dimTags_n;
+ int *dt1 = g1->gmsh_dimTags, *dt2 = g2->gmsh_dimTags;
+ if(pool1[i] == pool2[i]) continue;
+ /* Swap content according to flags. Don't swap refcount! */
+ if(flags & Scad_swap_name) {
+ if(dev->log) {
+ if(str_is_empty(&g1->name) && str_is_empty(&g2->name)) {
+ /* Do nothing */
+ }
+ else if(str_is_empty(&g1->name)) {
+ logger_print(dev->logger, dev->log_type,
+ "Swapping names for geometry %p and geometry '%s'.\n",
+ (void*)g1, str_cget(&g2->name));
+ logger_print(dev->logger, dev->log_type,
+ "Geometry '%s' is now unnamed geometry %p.\n",
+ str_cget(&g2->name), (void*)g2);
+ }
+ else if(str_is_empty(&g2->name)) {
+ logger_print(dev->logger, dev->log_type,
+ "Swapping names for geometry %p and geometry '%s'.\n",
+ (void*)g2, str_cget(&g1->name));
+ logger_print(dev->logger, dev->log_type,
+ "Geometry '%s' is now unnamed geometry %p.\n",
+ str_cget(&g1->name), (void*)g1);
+ } else { /* Both named */
+ logger_print(dev->logger, dev->log_type,
+ "Swapping names for geometries '%s' and '%s'.\n",
+ str_cget(&g1->name), str_cget(&g1->name));
+ }
+ }
+ if(!str_is_empty(&g1->name)) {
+ ERR(htable_names_set(&dev->geometry_names, &g1->name, &g2));
+ }
+ if(!str_is_empty(&g2->name)) {
+ ERR(htable_names_set(&dev->geometry_names, &g2->name, &g1));
+ }
+ if(!str_is_empty(&g1->name) || !str_is_empty(&g2->name)) {
+ ERR(str_copy(&tmp, &g1->name));
+ ERR(str_copy(&g1->name, &g2->name));
+ ERR(str_copy(&g2->name, &tmp));
+ }
+ }
+ if(flags & Scad_swap_geometry) {
+ /* Swap in tag2geom tables */
+ size_t n;
+ if(dev->log) {
+ if(str_is_empty(&g1->name) && str_is_empty(&g2->name)) {
+ logger_print(dev->logger, dev->log_type,
+ "Swapping tags for unnamed geometries %p and %p.\n",
+ (void*)g1, (void*)g2);
+ }
+ else if(str_is_empty(&g1->name)) {
+ logger_print(dev->logger, dev->log_type,
+ "Swapping tags for unnamed geometry %p and geometry '%s'.\n",
+ (void*)g1, str_cget(&g2->name));
+ }
+ else if(str_is_empty(&g2->name)) {
+ logger_print(dev->logger, dev->log_type,
+ "Swapping tags for unnamed geometry %p and geometry '%s'.\n",
+ (void*)g2, str_cget(&g1->name));
+ }
+ else {
+ logger_print(dev->logger, dev->log_type,
+ "Swapping tags for geometries '%s' and '%s'.\n",
+ str_cget(&g1->name), str_cget(&g1->name));
+ }
+ if(str_is_empty(&g1->name)) {
+ ERR(str_printf(&msg,
+ "Tags now registered against unnamed geometry '%p': ",
+ (void*)g1));
+ } else {
+ ERR(str_printf(&msg, "Tags now registered against geometry '%s': ",
+ str_cget(&g1->name)));
+ }
+ for(n = 0; n < c2; n += 2) {
+ int dim = dt2[n];
+ int tag = dt2[n+1];
+ if(n) { ERR(str_append_printf(&msg, ",")); }
+ ERR(str_append_printf(&msg, " %d.%d", dim, tag));
+ }
+ logger_print(dev->logger, dev->log_type, "%s.\n", str_cget(&msg));
+ if(str_is_empty(&g2->name)) {
+ ERR(str_printf(&msg,
+ "Tags now registered against unnamed geometry '%p': ",
+ (void*)g2));
+ } else {
+ ERR(str_printf(&msg, "Tags now registered against geometry '%s': ",
+ str_cget(&g2->name)));
+ }
+ for(n = 0; n < c1; n += 2) {
+ int dim = dt1[n];
+ int tag = dt1[n+1];
+ ERR(str_append_printf(&msg, (n ? ", %d.%d" : "%d.%d"), dim, tag));
+ }
+ logger_print(dev->logger, dev->log_type, "%s.\n", str_cget(&msg));
+ }
+ /* Swap tags */
+ SWAP(int*, g1->gmsh_dimTags, g2->gmsh_dimTags);
+ SWAP(size_t, g1->gmsh_dimTags_n, g2->gmsh_dimTags_n);
+ }
}
- memcpy(geom->gmsh_dimTags, tagout, tagoutn * sizeof(*tagout));
-
- ERR(device_register_tags(geom));
exit:
- if(out_geometry) *out_geometry = geom;
- if(allocator) {
- MEM_RM(allocator, data1);
- MEM_RM(allocator, data2);
- }
- gmshFree(mapn);
- gmshFree(tagout);
- free_gmsh_map(map, mapnn);
+ if(flags & Scad_swap_name) str_release(&tmp);
+ if(flags & Scad_swap_geometry && dev->log) str_release(&msg);
return res;
error:
- if(geom) {
- SCAD(geometry_ref_put(geom));
- geom = NULL;
- }
- if(tagout) gmshModelOccRemove(tagout, tagoutn, 1, &ierr);
goto exit;
}
@@ -1714,8 +2153,13 @@ scad_geometry_boundary
int* data = NULL;
int ierr = 0;
struct scad_geometry* geom = NULL;
- struct scad_device* dev = get_device();
struct mem_allocator* allocator = NULL;
+ struct scad_device* dev = get_device();
+ int log = (dev->options.Misc.LogRefCounting & Scad_log_dimTags_all);
+ enum log_type log_type = dev->log_type;
+ size_t i;
+ struct str msg;
+ int init = 0;
if(!geometries || !out_geometry) {
res = RES_BAD_ARG;
@@ -1729,7 +2173,7 @@ scad_geometry_boundary
gmshModelGetBoundary(data, sz, &tagout, &tagoutn, 1, 0, 0, &ierr);
ERR(gmsh_err_to_res_T(ierr));
- ERR(scad_geometry_create(name, &geom));
+ ERR(geometry_create(name, &geom));
geom->gmsh_dimTags_n = tagoutn;
geom->gmsh_dimTags = MEM_ALLOC(allocator, tagoutn * 2 * sizeof(*tagout));
if(!geom->gmsh_dimTags) {
@@ -1740,7 +2184,44 @@ scad_geometry_boundary
ERR(device_register_tags(geom));
+ if(log) {
+ str_init(allocator, &msg);
+ init = 1;
+ logger_print(dev->logger, log_type, "Boundary specific tag management:\n");
+ ERR(str_printf(&msg, " tags ["));
+ for(i = 0; i < tagoutn; i += 2) {
+ const int dim = tagout[i];
+ const int tag = tagout[i+1];
+ ERR(str_append_printf(&msg, (i ? ", %d.%d" : "%d.%d"), dim, tag));
+ }
+ ERR(str_append_printf(&msg, "] getting a ref to tags ["));
+ for(i = 0; i < sz; i += 2) {
+ const int dim = data[i];
+ const int tag = data[i+1];
+ ERR(str_append_printf(&msg, (i ? ", %d.%d" : "%d.%d"), dim, tag));
+ }
+ logger_print(dev->logger, log_type, "%s].\n", str_cget(&msg));
+ }
+ for(i = 0; i < tagoutn; i += 2) {
+ int dim = tagout[i];
+ int tag = tagout[i+1];
+ struct tag_desc* desc = device_get_description(dim, tag);
+ ASSERT(dim == 2);
+ if(!desc) {
+ res = RES_BAD_OP;
+ goto error;
+ }
+ /* Need to protect out_geometry's tags by getting a ref on input tags or
+ * deleting input geometry will possibly delete them */
+ ERR(device_register_ref_to_tags(dim, tag, data, sz));
+ /* As the 2D tags will be deleted when the 3D tag they are part of are
+ * deleted, they shouldn't be deleted when the geometry they belongs to are
+ * released. */
+ desc->delete_policy = Scad_do_not_delete;
+ }
+
exit:
+ if(init) str_release(&msg);
if(allocator) MEM_RM(allocator, data);
if(out_geometry) *out_geometry = geom;
gmshFree(tagout);
@@ -1797,9 +2278,9 @@ scad_step_import
if (name) {
ERR(str_set(&strname, name));
ERR(str_append_printf(&strname,"_%lu", (unsigned long)i));
- ERR(scad_geometry_create(str_cget(&strname), geom_array+i));
+ ERR(geometry_create(str_cget(&strname), geom_array+i));
} else {
- ERR(scad_geometry_create(NULL, geom_array+i));
+ ERR(geometry_create(NULL, geom_array+i));
}
geom_array[i]->gmsh_dimTags_n = 2;
@@ -1834,7 +2315,6 @@ error:
goto exit;
}
-
res_T
scad_geometry_normal
(struct scad_geometry* geom,
@@ -1846,15 +2326,18 @@ scad_geometry_normal
res_T res = RES_OK;
int ierr = 0;
size_t i;
- int* data = NULL;
+ const int* data = NULL;
size_t sz = 0;
- struct scad_geometry* surface = NULL;
struct scad_geometry* out = NULL;
struct scad_device* dev = get_device();
+ int log = (dev->options.Misc.LogRefCounting & Scad_log_dimTags_all);
+ enum log_type log_type = dev->log_type;
struct mem_allocator* allocator = NULL;
double* coord = NULL;
double* pcoord = NULL;
double* normals = NULL;
+ struct darray_int tags;
+ int initialized = 0;
if(!geom || !p || !N || !out_geometry) {
res = RES_BAD_ARG;
@@ -1864,36 +2347,32 @@ scad_geometry_normal
ERR(check_device(FUNC_NAME));
allocator = dev->allocator;
- if(geom->gmsh_dimTags[0] == 2) {
- ERR(scad_geometry_copy(geom, NULL, &surface));
- ERR(scad_synchronize());
- } else if(geom->gmsh_dimTags[0] == 3) {
- ERR(scad_geometry_boundary(NULL, &geom, 1, &surface));
- } else {
- res = RES_BAD_ARG;
- goto error;
- }
+ darray_int_init(dev->allocator, &tags);
+ initialized = 1;
- ERR(gather_tags(&surface, 1, &data, &sz));
+ ERR(get_2d_tags(geom, &tags));
+ data = darray_int_cdata_get(&tags);
+ sz = darray_int_size_get(&tags);
- for(i=0; sz/2; ++i) {
+ for(i = 0; i < sz; ++i) {
size_t pcoord_n;
size_t coord_n;
size_t normals_n;
- int dim = data[2*i];
- int tag = data[2*i + 1];
+ const int dim = 2;
+ int tag = data[i];
- gmshModelGetParametrization(2, tag, p, 3, &pcoord, &pcoord_n, &ierr);
- ERR(gmsh_err_to_res_T(ierr));
-
- gmshModelGetValue(2, tag, pcoord, pcoord_n, &coord, &coord_n, &ierr);
+ gmshModelGetClosestPoint(dim, tag, p, 3, &coord, &coord_n, &pcoord,
+ &pcoord_n, &ierr);
ERR(gmsh_err_to_res_T(ierr));
+ ASSERT(pcoord_n == (size_t)dim);
+ ASSERT(coord_n == 3);
if(d3_eq_eps(p, coord, 1e-6)) {
gmshModelGetNormal(tag, pcoord, pcoord_n, &normals, &normals_n, &ierr);
ERR(gmsh_err_to_res_T(ierr));
+ ASSERT(normals_n == 3);
- ERR(scad_geometry_create(name, &out));
+ ERR(geometry_create(name, &out));
out->gmsh_dimTags = MEM_ALLOC(allocator, 2 * sizeof(*out->gmsh_dimTags));
if(!out->gmsh_dimTags) {
res = RES_MEM_ERR;
@@ -1902,11 +2381,29 @@ scad_geometry_normal
out->gmsh_dimTags_n = 2;
out->gmsh_dimTags[0] = dim;
out->gmsh_dimTags[1] = tag;
+ d3_set(N, normals);
+
ERR(device_register_tags(out));
- d3_set(N, normals);
+ /* Need to protect geometries' tags or deleting out geometry will possibly
+ * delete them */
+ if(log) {
+ logger_print(dev->logger, log_type,
+ "Tag %d.%d getting a reference to other tags.\n", dim, tag);
+ }
+ ERR(device_register_ref_to_tags(dim, tag, geom->gmsh_dimTags,
+ geom->gmsh_dimTags_n));
+ if(log) {
+ logger_print(dev->logger, log_type,
+ "Tag %d.%d getting a reference to other tags done.\n", dim, tag);
+ }
+
break;
}
+
+ gmshFree(coord);
+ gmshFree(pcoord);
+ coord = pcoord = NULL;
}
if(!out) { /* Could not find a matching surface */
res = RES_BAD_ARG;
@@ -1917,9 +2414,8 @@ exit:
gmshFree(coord);
gmshFree(pcoord);
gmshFree(normals);
+ if(initialized) darray_int_release(&tags);
if(out_geometry) *out_geometry = out;
- if(allocator) MEM_RM(allocator, data);
- if(surface) SCAD(geometry_ref_put(surface));
return res;
error:
goto exit;
@@ -1927,33 +2423,34 @@ error:
res_T
scad_geometry_dilate
- (struct scad_geometry* geom,
- double center[3],
- double scale[3])
+ (const struct scad_geometry* geom,
+ const double center[3],
+ const double scale[3],
+ const char* name,
+ struct scad_geometry** out_geometry)
{
res_T res = RES_OK;
int ierr = 0;
- int* data = NULL;
- size_t sz = 0;
- struct scad_device* dev = get_device();
- struct mem_allocator* allocator = NULL;
+ struct scad_geometry* out = NULL;
- if(!geom || !scale|| !center) {
+ if(!geom || !scale|| !center || ! out_geometry) {
res = RES_BAD_ARG;
goto error;
}
ERR(check_device(FUNC_NAME));
- allocator = dev->allocator;
- ERR(gather_tags(&geom, 1, &data, &sz));
+ ERR(scad_geometry_copy(geom, name, &out));
- gmshModelOccDilate(data, sz, SPLIT3(center), SPLIT3(scale), &ierr);
+ gmshModelOccDilate(out->gmsh_dimTags, out->gmsh_dimTags_n, SPLIT3(center),
+ SPLIT3(scale), &ierr);
ERR(gmsh_err_to_res_T(ierr));
exit:
- if(allocator) MEM_RM(allocator, data);
+ if(out_geometry) *out_geometry = out;
return res;
error:
+ if(out) SCAD(geometry_ref_put(out));
+ out = NULL;
goto exit;
}
diff --git a/src/scad_geometry.h b/src/scad_geometry.h
@@ -18,9 +18,7 @@
#include <stdlib.h>
-#include <rsys/rsys.h>
#include <rsys/str.h>
-#include <rsys/logger.h>
#include <rsys/ref_count.h>
struct mem_allocator;
@@ -29,6 +27,8 @@ struct scad_geometry {
int* gmsh_dimTags;
size_t gmsh_dimTags_n;
struct str name;
+ void* custom;
+ void (*release) (void* data);
ref_T ref;
};
diff --git a/src/test_api.c b/src/test_api.c
@@ -56,6 +56,7 @@ main(int argc, char* argv[])
struct mem_allocator allocator;
struct logger logger;
struct darray_double trg;
+ struct scad_options options = SCAD_DEFAULT_OPTIONS;
const char* name;
size_t i, c;
@@ -103,6 +104,10 @@ main(int argc, char* argv[])
BAD(scad_initialize(&logger, &allocator, -1));
OK(scad_initialize(&logger, &allocator, 3));
+
+ OK(scad_set_options(NULL));
+ OK(scad_set_options(&options));
+
OK(scad_add_sphere("sphere 1", p1, .1, &geom1));
OK(scad_add_sphere(NULL, p2, .2, &geom2));
geoms[0] = geom1;
@@ -213,14 +218,32 @@ main(int argc, char* argv[])
BAD(scad_geometry_rename(geom2, "sphere 1")); /* Name already in use */
OK(scad_geometry_rename(geom2, NULL));
- BAD(scad_geometry_translate(NULL, d1));
- BAD(scad_geometry_translate(geom1, NULL));
- OK(scad_geometry_translate(geom1, d1));
+ BAD(scad_geometry_translate(NULL, NULL, NULL, NULL));
+ BAD(scad_geometry_translate(NULL, NULL, NULL, &geom));
+ BAD(scad_geometry_translate(NULL, d1, NULL, NULL));
+ BAD(scad_geometry_translate(NULL, d1, NULL, &geom));
+ BAD(scad_geometry_translate(geom1, NULL, NULL, NULL));
+ BAD(scad_geometry_translate(geom1, NULL, NULL, &geom));
+ BAD(scad_geometry_translate(geom1, d1, NULL, NULL));
+ OK(scad_geometry_translate(geom1, d1, NULL, &geom));
+ OK(scad_geometry_ref_put(geom));
- BAD(scad_geometry_rotate(NULL, p1, d1, 1));
- BAD(scad_geometry_rotate(geom1, NULL, d1, 1));
- BAD(scad_geometry_rotate(geom1, p1, NULL, 1));
- OK(scad_geometry_rotate(geom1, p1, d1, 1));
+ BAD(scad_geometry_rotate(NULL, NULL, NULL, 1, NULL, NULL));
+ BAD(scad_geometry_rotate(NULL, NULL, NULL, 1, NULL, &geom));
+ BAD(scad_geometry_rotate(NULL, NULL, d1, 1, NULL, NULL));
+ BAD(scad_geometry_rotate(NULL, NULL, d1, 1, NULL, &geom));
+ BAD(scad_geometry_rotate(NULL, p1, NULL, 1, NULL, NULL));
+ BAD(scad_geometry_rotate(NULL, p1, NULL, 1, NULL, &geom));
+ BAD(scad_geometry_rotate(NULL, p1, d1, 1, NULL, NULL));
+ BAD(scad_geometry_rotate(NULL, p1, d1, 1, NULL, &geom));
+ BAD(scad_geometry_rotate(geom1, NULL, NULL, 1, NULL, NULL));
+ BAD(scad_geometry_rotate(geom1, NULL, NULL, 1, NULL, &geom));
+ BAD(scad_geometry_rotate(geom1, NULL, d1, 1, NULL, NULL));
+ BAD(scad_geometry_rotate(geom1, NULL, d1, 1, NULL, &geom));
+ BAD(scad_geometry_rotate(geom1, p1, NULL, 1, NULL, NULL));
+ BAD(scad_geometry_rotate(geom1, p1, NULL, 1, NULL, &geom));
+ BAD(scad_geometry_rotate(geom1, p1, d1, 1, NULL, NULL));
+ OK(scad_geometry_rotate(geom1, p1, d1, 1, NULL, &geom));
BAD(scad_geometry_extrude(NULL, NULL, d1, &geom));
BAD(scad_geometry_extrude(poly, NULL, NULL, &geom));
@@ -247,39 +270,33 @@ main(int argc, char* argv[])
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));
+ BAD(scad_stl_data_write(NULL, NULL, Scad_keep_normals_unchanged, 0));
+ BAD(scad_stl_data_write(&trg, NULL, Scad_keep_normals_unchanged, 0));
+ BAD(scad_stl_data_write(NULL, "/tmp/test", Scad_keep_normals_unchanged, 0));
+ OK(scad_stl_data_write(&trg, "/tmp/test", Scad_keep_normals_unchanged, 0));
+ OK(scad_stl_data_write(&trg, "/tmp/test", Scad_keep_normals_unchanged, 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_stl_export(NULL, NULL, Scad_keep_normals_unchanged, 0));
+ BAD(scad_stl_export(geom2, NULL, Scad_keep_normals_unchanged, 0)); /* geom2 has no name */
+ OK(scad_stl_export(geom2, "/tmp/test", Scad_keep_normals_unchanged, 0));
+ OK(scad_stl_export(geom2, "/tmp/test", Scad_force_normals_inward, 1));
+ OK(scad_stl_export(geom1, NULL, Scad_force_normals_outward, 1));
+
+ BAD(scad_stl_export_split(NULL, NULL, Scad_keep_normals_unchanged, 0));
+ BAD(scad_stl_export_split(geom2, NULL, Scad_keep_normals_unchanged, 0)); /* geom2 has no name */
+ OK(scad_stl_export_split(geom2, "/tmp/test", Scad_keep_normals_unchanged, 0));
+ OK(scad_stl_export_split(geom2, "/tmp/testb", Scad_keep_normals_unchanged, 1));
+ OK(scad_stl_export_split(geom1, NULL, Scad_keep_normals_unchanged, 0));
+ OK(scad_stl_export_split(geom1, "different_names", Scad_keep_normals_unchanged, 1));
BAD(scad_geometry_get_name(NULL, &name));
BAD(scad_geometry_get_name(geom1, NULL));
OK(scad_geometry_get_name(geom1, &name));
OK(scad_geometry_get_name(geom2, &name));
- logger_release(&logger);
OK(scad_finalize());
+ logger_release(&logger);
check_memory_allocator(&allocator);
mem_shutdown_proxy_allocator(&allocator);
diff --git a/src/test_export.c b/src/test_export.c
@@ -42,7 +42,6 @@ main(int argc, char* argv[])
OK(mem_init_proxy_allocator(&allocator, &mem_default_allocator));
OK(logger_init(&allocator, &logger));
- logger_release(&logger);
OK(scad_initialize(&logger, &allocator, 3));
OK(scad_add_rectangle("rectangle", p1, d1, &rectangle));
@@ -54,17 +53,21 @@ main(int argc, char* argv[])
OK(scad_scene_mesh());
- OK(scad_stl_export(rectangle, NULL, 0));
- OK(scad_stl_export(rectangle, "bin_rectangle", 1));
+ /* Do not define a volume */
+ BAD(scad_stl_export(rectangle, "not-a-volume.stl", Scad_force_normals_outward, 0));
+
+ OK(scad_stl_export(rectangle, NULL, Scad_keep_normals_unchanged, 0));
+ OK(scad_stl_export(rectangle, "bin_rectangle", Scad_keep_normals_unchanged, 1));
- OK(scad_stl_export(cube, NULL, 0));
- OK(scad_stl_export(cube, "bin_cube", 1));
+ OK(scad_stl_export(cube, NULL, Scad_force_normals_outward, 0));
+ OK(scad_stl_export(cube, "bin_cube", Scad_force_normals_outward, 1));
- OK(scad_stl_export(cube2, NULL, 0));
- OK(scad_stl_export(cube2, "bin_cube2", 1));
+ OK(scad_stl_export(cube2, NULL, Scad_force_normals_outward, 0));
+ OK(scad_stl_export(cube2, "bin_cube2", Scad_force_normals_outward, 1));
OK(scad_finalize());
+ logger_release(&logger);
check_memory_allocator(&allocator);
mem_shutdown_proxy_allocator(&allocator);
CHK(mem_allocated_size() == 0);
diff --git a/src/test_lifetime.c b/src/test_lifetime.c
@@ -0,0 +1,135 @@
+/* Copyright (C) 2022 |Meso|Star> (contact@meso-star.com)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "scad.h"
+#include "scad_geometry.h"
+#include "test_common.h"
+
+#include <rsys/rsys.h>
+#include <rsys/str.h>
+#include <rsys/math.h>
+#include <rsys/mem_allocator.h>
+#include <rsys/logger.h>
+#include <rsys/dynamic_array_double.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+
+static void
+alive_and_well
+ (struct scad_geometry* g,
+ struct mem_allocator* allocator)
+{
+ struct scad_geometry** geom_array = NULL;
+ size_t i, c;
+
+ ASSERT(g && allocator);
+
+ OK(scad_geometry_explode(g, "alive_and_well", &geom_array, &c));
+ OK(scad_geometry_ref_put(g));
+ OK(scad_synchronize());
+ for(i = 0; i < c; i++) {
+ double dir1[3] = {0, 0, 1};
+ struct scad_geometry* gg;
+ OK(scad_geometry_extrude(geom_array[i], "a_n_w_extruded", dir1, &gg));
+ OK(scad_geometry_ref_put(geom_array[i]));
+ OK(scad_geometry_ref_put(gg));
+ }
+ MEM_RM(allocator, geom_array);
+}
+
+int
+main(int argc, char* argv[])
+{
+ res_T res = RES_OK;
+ double p1[3] = {0, 0, 0};
+ double diago[] = {1, 1, 1}, diago_[] = {.5, .5, -1}, base[] = {1, 1, 0};
+ double dir1[3] = {0, 0, 1};
+ struct scad_geometry* geom1 = NULL;
+ struct scad_geometry* geom2 = NULL;
+ struct scad_geometry* geom = NULL;
+ struct scad_geometry* geoms[2];
+ struct scad_geometry* out_geoms[2];
+ struct mem_allocator allocator;
+ struct scad_geometry** list = NULL;
+ size_t list_n, center_n, i;
+ double center[3], N[3];
+ struct scad_options options = SCAD_DEFAULT_OPTIONS;
+
+ (void)argc; (void)argv;
+
+ OK(mem_init_proxy_allocator(&allocator, &mem_default_allocator));
+
+ OK(scad_initialize(NULL, &allocator, 3));
+ options.Misc.LogRefCounting = Scad_log_dimTags_all | Scad_log_geometry;
+ OK(scad_set_options(&options));
+
+ /* Check that 2D constituants of a deleted 3D object are alive and well */
+ OK(scad_add_box("box", p1, diago, &geom));
+ OK(scad_geometry_boundary("boundary", &geom, 1, &geom1));
+ OK(scad_geometry_ref_put(geom));
+ alive_and_well(geom1, &allocator);
+
+ /* Check that a 3D derivative of a deleted 2D object is alive and well */
+ OK(scad_add_rectangle("rect", p1, base, &geom));
+ OK(scad_geometry_extrude(geom, "cube", dir1, &geom1));
+ OK(scad_geometry_ref_put(geom));
+ OK(scad_geometry_boundary("boundary", &geom1, 1, &geom2));
+ OK(scad_geometry_ref_put(geom1));
+ alive_and_well(geom2, &allocator);
+
+ /* Check that a 2D part of a deleted 3D object is alive and well */
+ OK(scad_add_rectangle("rect2", p1, base, &geom));
+ OK(scad_geometry_extrude(geom, "cube2", dir1, &geom1));
+ OK(scad_geometry_ref_put(geom1));
+ alive_and_well(geom, &allocator);
+
+ OK(scad_add_box("cavity", p1, diago, &geom1));
+ OK(scad_geometry_boundary("bcavity", &geom1, 1, &geom2));
+ OK(scad_geometry_explode(geom2, "explode", &list, &list_n));
+ OK(scad_geometry_ref_put(geom2));
+ OK(scad_geometry_get_count(list[0], ¢er_n));
+ ASSERT(center_n == 1);
+ OK(scad_geometry_get_centerofmass(list[0], center));
+ OK(scad_geometry_normal(list[0], center, N, "surface", &geom));
+ OK(scad_geometry_ref_put(geom));
+ for(i = 0; i < list_n; i++) {
+ OK(scad_geometry_ref_put(list[i]));
+ }
+ MEM_RM(&allocator, list);
+ OK(scad_geometry_ref_put(geom1));
+
+ /* Check that 2D constituants of a deleted 3D object are alive and well after
+ * a partition */
+ OK(scad_add_box("box1", p1, diago, geoms+0));
+ OK(scad_add_box("box2", p1, diago_, geoms+1));
+ OK(scad_geometry_boundary(NULL, geoms+0, 1, &geom1));
+ OK(scad_geometry_boundary(NULL, geoms+1, 1, &geom2));
+ OK(scad_geometries_partition(geoms, 2, 0, out_geoms));
+ OK(scad_geometry_ref_put(geoms[0]));
+ OK(scad_geometry_ref_put(geoms[1]));
+ alive_and_well(geom1, &allocator);
+ alive_and_well(geom2, &allocator);
+ OK(scad_geometry_ref_put(out_geoms[0]));
+ OK(scad_geometry_ref_put(out_geoms[1]));
+
+ OK(scad_finalize());
+
+ check_memory_allocator(&allocator);
+ mem_shutdown_proxy_allocator(&allocator);
+ CHK(mem_allocated_size() == 0);
+
+ return (res == RES_OK) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/src/test_partition.c b/src/test_partition.c
@@ -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));
+ OK(scad_stl_export(out_geoms[0], name, Scad_force_normals_outward, 0));
snprintf(name, sizeof(name), "part_%g_2o", x);
- OK(scad_stl_export(out_geoms[1], name, 1));
+ OK(scad_stl_export(out_geoms[1], name, Scad_force_normals_outward, 1));
} else {
snprintf(name, sizeof(name), "part_%g_1", x);
- OK(scad_stl_export(geoms[0], name, 0));
+ OK(scad_stl_export(geoms[0], name, Scad_force_normals_outward, 0));
snprintf(name, sizeof(name), "part_%g_2", x);
- OK(scad_stl_export(geoms[1], name, 1));
+ OK(scad_stl_export(geoms[1], name, Scad_force_normals_outward, 1));
}
end: