star-cad

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

commit eca24493f249ca8cb459814e53236b227489c90a
parent 63e24b5c7dd1b4dcd181559e418c09ac6026e1a9
Author: Christophe Coustet <christophe.coustet@meso-star.com>
Date:   Fri, 10 Oct 2025 11:00:46 +0200

Merge branch 'release_0.6'

Diffstat:
MMakefile | 4+++-
MREADME.md | 37++++++++++++++++++++++++++++++++++---
Mconfig.mk | 4++--
Msrc/scad.c | 68++++++++++++++++++++++++++++++++++++++++++++++++++------------------
Msrc/scad.h | 759+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
Msrc/scad_device.c | 215++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
Msrc/scad_device.h | 9+++++++++
Msrc/scad_geometry.c | 1211+++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------
Msrc/test_api.c | 1658+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
Msrc/test_export.c | 41++++++++++++++++++++++++++---------------
Msrc/test_export2.c | 26++++++++++++++++----------
Msrc/test_lifetime.c | 76++++++++++++++++++++++++++++++++++++++++++++++------------------------------
Asrc/test_normals.c | 97+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/test_partition.c | 31+++++++++++++++++++------------
Msrc/test_periodic.c | 50++++++++++++++++++++++++++------------------------
Asrc/test_sync.c | 169+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/test_tolerance.c | 99+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
17 files changed, 3494 insertions(+), 1060 deletions(-)

diff --git a/Makefile b/Makefile @@ -131,7 +131,8 @@ TEST_SRC =\ src/test_export2.c\ src/test_lifetime.c\ src/test_partition.c\ - src/test_periodic.c + src/test_periodic.c\ + src/test_sync.c TEST_OBJ = $(TEST_SRC:.c=.o) TEST_DEP = $(TEST_SRC:.c=.d) @@ -185,5 +186,6 @@ test_export2 \ test_lifetime \ test_partition \ test_periodic \ +test_sync \ : config.mk scad-local.pc $(LIBNAME) $(CC) $(CFLAGS_EXE) -o $@ src/$@.o $(LDFLAGS_EXE) $(SCAD_LIBS) $(RSYS_LIBS) -lm diff --git a/README.md b/README.md @@ -25,6 +25,39 @@ Edit config.mk as needed, then run: ## Release notes +### Version 0.6 + +- Add `scad_add_cone' and `scad_add_torus' API calls. +- API changes for geometry creation: geometry name is no longer provided to the + various add_<something> functions, that now create unnamed geometries; instead + names have to be set after geometry creation through `scad_geometry_set_name'. +- API change for `scad_geometries_boundaries' and + `scad_geometries_common_boundaries': now return a list of geometries + (that could be grouped through `scad_collect_geometries'); used to return a + single geometry containing the (possibly empty) boundary elements (that could + be ungrouped using `scad_geometry_explode'). +- API uniformization (involving args order, naming, etc.). +- Fix geometry naming. Now get_name returns NULL if name was set to NULL or + let unset (used to return ""). Allow to set same name again (to the same + geometry) without triggering an error. +- Fix `scad_geometry_get_closest_point'. The returned point was not correct. +- Fix `scad_geometry_get_normal' to get the normal at the geometry closest + point, fixing a memleak in the process. +- Fix `scad_scene_clear' (geometries with multiple references where not + released). +- Fix `scad_geometries_common_boundaries'. Used to trigger OccIntersect that, as + a side effet, partitionned geometries. +- Fix `scad_step_import' (arguments where inverted). +- Change `scad_geometry_get_centerofmass'. Now returns the unique center of mass + of all the entities involved in the geometry, regardless of their count (used + to return one center of mass per entity in the geometry). +- Rework the auto-sync feature (that manages sync events between gmsh and + Open Cascade); this should greatly improve performance. +- Rename enum members in capital letters. +- Upgrade gmsh dependency (to 1.14.1) and Open-Cascade dependency (to 7.9.1). +- Improve code robustness. +- Improve tests. + ### Version 0.5.2 - Fix a file name in debug STL output file. @@ -42,8 +75,6 @@ Edit config.mk as needed, then run: - Add the `automatic' meshing algorithm. - Add the `initial mesh only' meshing algorithm. - Add a geometry visibility management feature. -- Add `' API call. -- Add `' API call. ### Version 0.5 @@ -56,7 +87,7 @@ Edit config.mk as needed, then run: ### Version 0.4.1 -- Add `scad_dump_geometry` API call that can be called from a debugging +- Add `scad_dump_geometry' API call that can be called from a debugging session - Remove an invalid assert - Fix build that used gmsh library in Release when Debug should have diff --git a/config.mk b/config.mk @@ -1,4 +1,4 @@ -VERSION = 0.5.2 +VERSION = 0.6 PREFIX = /usr/local LIB_TYPE = SHARED @@ -23,7 +23,7 @@ RANLIB = ranlib PCFLAGS_STATIC = --static PCFLAGS = $(PCFLAGS_$(LIB_TYPE)) -GMSH_VERSION = 4.12.2 +GMSH_VERSION = 4.14.1 GMSH_CFLAGS = $$($(PKG_CONFIG) $(PCFLAGS) --cflags gmsh) GMSH_LIBS = $$($(PKG_CONFIG) $(PCFLAGS) --libs gmsh) diff --git a/src/scad.c b/src/scad.c @@ -189,7 +189,9 @@ error: goto exit; } -/* Accumulate tags of geometry in tags */ +/* Accumulate the 2D tags of geometry (if it is 2D) or the tags of its boundary + * (if it is 3D). + * Trigger a call to sync_device() */ res_T get_2d_tags (struct scad_geometry* geometry, @@ -208,6 +210,7 @@ get_2d_tags data = geometry->gmsh_dimTags; ASSERT(sz % 2 == 0); + ERR(sync_device()); for(i = 0; i < sz; i += 2) { int dim = data[i]; int tag = data[i+1]; @@ -265,7 +268,7 @@ scad_synchronize } gmshModelOccSynchronize(&ierr); - get_device()->need_synchro = dev->options.Misc.DebugOpenCascadeSync; + dev->need_synchro = dev->options.Misc.DebugAutoSync; ERR(gmsh_err_to_res_T(ierr)); exit: @@ -291,11 +294,14 @@ scad_run_ui(void) goto error; } - if(dev->options.Misc.SynchronizeOnRunUI && get_device()->need_synchro) { - ERR(scad_synchronize()); + if(dev->options.Misc.SynchronizeOnRunUI) { + ERR(sync_device()); } gmshFltkRun(&ierr); + if(ierr) { + log_error(dev, "Cannot call FLTK: you probably need to build gmsh locally.\n"); + } ERR(gmsh_err_to_res_T(ierr)); exit: @@ -309,13 +315,15 @@ scad_get_dimtag_refcount (const int dim, const int tag) { - struct tag_desc* desc = device_get_description(dim, tag); + struct tag_desc* desc = NULL; + + if(check_device(FUNC_NAME) != RES_OK) return SIZE_MAX; + desc = device_get_description(dim, tag); if(!desc) return SIZE_MAX; return desc->refcount; } - res_T scad_scene_write (const char* name) @@ -328,6 +336,8 @@ scad_scene_write goto error; } + ERR(check_device(FUNC_NAME)); + ERR(sync_device()); gmshWrite(name, &ierr); ERR(gmsh_err_to_res_T(ierr)); @@ -365,7 +375,7 @@ scad_stl_get_data_partial int tags_initialized = 0; res_T res = RES_OK; - if(!geometry || !triangles) { + if(!geometry || !triangles || (!dont && dont_count > 0)) { res = RES_BAD_ARG; goto error; } @@ -425,8 +435,13 @@ scad_stl_get_data_partial } ASSERT(tcount == darray_double_size_get(triangles)); if(count0 == tcount) { - log_message(dev, "No triangle collected for geometry '%s'.\n", - str_cget(&geometry->name)); + if(str_is_empty(&geometry->name)) { + log_message(dev, "No triangle collected for unnamed geometry '%p'.\n", + (void*)geometry); + } else { + log_message(dev, "No triangle collected for geometry '%s'.\n", + str_cget(&geometry->name)); + } } exit: @@ -509,7 +524,7 @@ scad_stl_sort_orientation res = RES_BAD_ARG; goto error; } - if(coord_n == 0 || orientation == Scad_keep_normals_unchanged) { + if(coord_n == 0 || orientation == SCAD_KEEP_NORMALS_UNCHANGED) { goto exit; } @@ -534,7 +549,7 @@ scad_stl_sort_orientation "Triangles duplicates found when sorting out normals (%u / %u) in set '%s'.\n", tcount_in - utcount_in, tcount_in, set_name); } - if(orientation == Scad_force_normals_outward) + 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; @@ -642,7 +657,7 @@ scad_stl_sort_orientation str_set(&dbl_name, set_name); str_insert(&dbl_name, idx - 4, "_double_sided_triangles"); r = scad_stl_data_write(&dblsided, str_cget(&dbl_name), - Scad_keep_normals_unchanged, 0); + SCAD_KEEP_NORMALS_UNCHANGED, 0); if(r == RES_OK) { log_error(get_device(), "Saved double sided triangles to file '%s'.\n", @@ -751,6 +766,8 @@ scad_stl_data_write goto error; } + ERR(check_device(FUNC_NAME)); + coord_n = darray_double_size_get(triangles); if(coord_n % 9) { res = RES_BAD_ARG; @@ -789,7 +806,11 @@ scad_stl_export struct str filename; int initialized = 0; - if(!geometry || (!file_name && str_is_empty(&geometry->name))) { + if(!geometry || (!file_name && str_is_empty(&geometry->name)) || + (orientation != SCAD_KEEP_NORMALS_UNCHANGED && + orientation != SCAD_FORCE_NORMALS_OUTWARD && + orientation != SCAD_FORCE_NORMALS_INWARD)) + { res = RES_BAD_ARG; goto error; } @@ -837,7 +858,12 @@ scad_stl_export_partial struct str filename; int initialized = 0; - if(!geometry) { + if(!geometry || (!file_name && str_is_empty(&geometry->name)) || + (!dont && dont_count > 0) || + (orientation != SCAD_KEEP_NORMALS_UNCHANGED && + orientation != SCAD_FORCE_NORMALS_OUTWARD && + orientation != SCAD_FORCE_NORMALS_INWARD)) + { res = RES_BAD_ARG; goto error; } @@ -880,9 +906,10 @@ scad_stl_export_split size_t i; struct scad_geometry** parts = NULL; struct scad_device* dev = get_device(); - size_t count; + size_t cpt = 0, count; + struct str name; + int init = 0; res_T res = RES_OK; - (void)binary; if(!geometry || (!file_name && str_is_empty(&geometry->name))) { res = RES_BAD_ARG; @@ -891,10 +918,13 @@ scad_stl_export_split ERR(check_device(FUNC_NAME)); - ERR(scad_geometry_explode(geometry, file_name, &parts, &count)); + ERR(scad_geometry_explode(geometry, &parts, &count)); ASSERT(count*2 == geometry->gmsh_dimTags_n); + str_init(dev->allocator, &name); + init = 1; for(i = 0; i < count; i++) { - ERR(scad_stl_export(parts[i], NULL, orientation, binary)); + ERR(str_printf(&name, "%s_%ld", file_name, cpt++)); + ERR(scad_stl_export(parts[i], str_cget(&name), orientation, binary)); } exit: @@ -902,6 +932,7 @@ exit: for(i = 0; i < count; i++) SCAD(geometry_ref_put(parts[i])); MEM_RM(dev->allocator, parts); } + if(init) str_release(&name); return res; error: goto exit; @@ -916,6 +947,7 @@ scad_scene_mesh ERR(check_device(FUNC_NAME)); + ERR(sync_device()); gmshModelMeshGenerate(2, &ierr); ERR(gmsh_err_to_res_T(ierr)); diff --git a/src/scad.h b/src/scad.h @@ -47,51 +47,44 @@ struct scad_geometry; /* Wrapping of dimTags gmsh description */ /* Verbosity levels */ enum scad_verbosity_levels { - scad_verbosity_fatal_errors = 0, - Scad_verbosity_errors = 1, - Scad_verbosity_warnings = 2, - Scad_verbosity_direct = 3, - Scad_verbosity_information = 4, - Scad_verbosity_status = 5, - Scad_verbosity_debug = 99 + SCAD_VERBOSITY_FATAL_ERRORS = 0, + SCAD_VERBOSITY_ERRORS = 1, + SCAD_VERBOSITY_WARNINGS = 2, + SCAD_VERBOSITY_DIRECT = 3, + SCAD_VERBOSITY_INFORMATION = 4, + SCAD_VERBOSITY_STATUS = 5, + SCAD_VERBOSITY_DEBUG = 99 }; /* Mesh algorithms */ enum scad_mesh_algorithm { - Scad_meshAdapt = 1, - Scad_Automatic = 2, /* Delaunay on planes, meshAdapt elsewhere */ - Scad_Initial_Mesh_Only = 3, /* Avoid new point creation */ - Scad_Delaunay = 5, - Scad_frontal_Delaunay = 6, - Scad_Quasi_Structured = 11 + SCAD_MESHADAPT = 1, + SCAD_AUTOMATIC = 2, /* Delaunay on planes, mesh adapt elsewhere */ + SCAD_INITIAL_MESH_ONLY = 3, /* Avoid new point creation */ + SCAD_DELAUNAY = 5, + SCAD_FRONTAL_DELAUNAY = 6, + SCAD_QUASI_STRUCTURED = 11 }; enum scad_sizes_extend_from_boundary { - Scad_never = 0, - Scad_surfaces_and_volumes = 1, - Scad_surfaces_and_volumes_smallest = 2, - Scad_surfaces_only = -2, - Scad_volumes_only = -3 + SCAD_NEVER = 0, + SCAD_SURFACES_AND_VOLUMES = 1, + SCAD_SURFACES_AND_VOLUMES_SMALLEST = 2, + SCAD_SURFACES_ONLY = -2, + SCAD_VOLUMES_ONLY = -3 }; enum scad_stl_solids { - Scad_single_solid = 0, - Scad_one_solid_per_surface = 1, - Scad_one_solid_per_physical_surface = 2 + SCAD_SINGLE_SOLID = 0, + SCAD_ONE_SOLID_PER_SURFACE = 1, + SCAD_ONE_SOLID_PER_PHYSICAL_SURFACE = 2 }; enum scad_log_refcounting { - 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 the kind of mesh size specification set by a call to the - * scad_geometries_set_mesh_size_modifier API call */ -enum scad_size_modifier_type { - Scad_absolute_size, - Scad_size_factor + 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 */ @@ -116,42 +109,59 @@ struct scad_options { int OCCParallel; } Geometry; struct { - int Step; /* Run UI when entering any scad API function; requires a FLTK-enabled gmsh build */ + /* Run UI when entering any star-cad API call; requires a FLTK-enabled gmsh + * build, possibly a local build of gmsh and OCC */ + int RunUIAtEachStep; + /* Call synchronize first when run_ui is called */ int SynchronizeOnRunUI; + /* Log ref counting operations on geometries (lot of logs expected) */ enum scad_log_refcounting LogRefCounting; - int DebugOpenCascadeSync; /* Systematic call to synchronize; if results change there is a sync bug in star-cad! */ + /* Triggers a synchronize operation before any star-cad API call. If results + * change, there is a bug in star-cad auto-synchronize mechanism. + * This slows down star-cad a lot! */ + int DebugAutoSync; + /* Check gmsh and OCC contexts are empty everytime star-cad context is + * empty. As a side effect, triggers a synchronize operation. */ + int DebugEmptyContext; } Misc; }; #define SCAD_DEFAULT_OPTIONS__ \ { { 1, 36, 1e+22, 1e-6, 1, 1, 1, \ - Scad_frontal_Delaunay, \ - Scad_surfaces_and_volumes, \ - Scad_one_solid_per_physical_surface }, \ - { Scad_verbosity_errors, 1 }, \ + SCAD_FRONTAL_DELAUNAY, \ + SCAD_SURFACES_AND_VOLUMES, \ + SCAD_ONE_SOLID_PER_PHYSICAL_SURFACE }, \ + { SCAD_VERBOSITY_ERRORS, 1 }, \ { 1 }, \ - { 0, 0, Scad_log_none, 0 } \ + { 0, 0, SCAD_LOG_NONE, 0, 0 } \ } static const struct scad_options SCAD_DEFAULT_OPTIONS = SCAD_DEFAULT_OPTIONS__; +/* A type to specify the kind of mesh size specification set by a call to the + * scad_geometries_set_mesh_size_modifier API call */ +enum scad_size_modifier_type { + SCAD_ABSOLUTE_SIZE, + SCAD_SIZE_FACTOR +}; + /* 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) + 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 + SCAD_KEEP_NORMALS_UNCHANGED, /* The only one allowed with non closed shapes */ + SCAD_FORCE_NORMALS_OUTWARD, + SCAD_FORCE_NORMALS_INWARD }; /* A type to specify how partitioning is done. */ enum scad_partition_flags { - Scad_allow_overlapping = BIT(0), - Scad_dump_on_overlapping_error = BIT(1) + SCAD_ALLOW_OVERLAPPING = BIT(0), + SCAD_DUMP_ON_OVERLAPPING_ERROR = BIT(1) /* Dump individual geometries to STL */ }; BEGIN_DECLS @@ -170,228 +180,368 @@ scad_initialize 2 = errors and warnings, 3 = errors, warnings, and informative messages */ +/* Close the session and release any geometry not yet released. + * Once finalized, scad can be initialized again. */ SCAD_API res_T scad_finalize (void); +/* Set global options `options' for scad. */ SCAD_API res_T scad_set_options (const struct scad_options* options); /* May be NULL: set default */ +/* Get global options for scad. */ SCAD_API res_T scad_get_options (struct scad_options* options); /******************************************************************************* - * Geometry API - A geometry is a primitive, a group of primitives, or the - * result of an operation on geometries. - * If provided, name must be unique. + * Scene API - + * All these calls process the scene (i.e. all the existing geomeries) at once. ******************************************************************************/ +/* Get the number of geometries in the scene */ SCAD_API res_T -scad_geometry_ref_get - (struct scad_geometry* geom); - -SCAD_API res_T -scad_geometry_ref_put - (struct scad_geometry* geom); +scad_scene_count + (size_t* count); +/* Clear the scene from all its geometries */ SCAD_API res_T scad_scene_clear (void); -/* Get the number of components of the geometry `geom' */ -SCAD_API res_T -scad_geometry_get_count - (const struct scad_geometry* geom, - size_t* count); - -/* 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') */ -SCAD_API res_T -scad_geometry_get_name - (const struct scad_geometry* geom, - const char** name); - -/* 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_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. */ -SCAD_API res_T -scad_geometry_get_mass - (struct scad_geometry* geom, - double* mass); - -/* Get the center of mass of the various components of the geometry. - * Note that `center' must be allocated by the caller with enough room for (at - * least) 3 times the count of geom (scad_geometry_get_count) doubles */ +/* Write the whole scene in a format that depends on filename extension. + * Available formats include "brep", "msh" (gmsh-specific format), "step", + * "stl", "vtk", etc. */ SCAD_API res_T -scad_geometry_get_centerofmass - (struct scad_geometry* geom, - double* center); +scad_scene_write + (const char* filename); +/* Create the mesh of the whole scene. + * Note that, due to gmsh capabilities, there is no way to mesh a given list of + * geometries. To avoid meshing useless geometries you can either release them + * using scad_geometry_ref_put before meshing, or turn them not-visible using + * visibility API. */ SCAD_API res_T -scad_geometry_get_closest_point - (struct scad_geometry* geom, - const double* from, - double* closest, - double* closest_distance); +scad_scene_mesh + (void); -/* Get the Boundig Box of geometry `geom' */ -SCAD_API res_T -scad_geometry_get_bounding_box - (struct scad_geometry* geom, - double min[3], - double max[3]); +/******************************************************************************* + * Geometry API - A geometry is a primitive, a group of primitives, or the + * result of an operation on geometries. + ******************************************************************************/ /* Add a rectangle to the scene, defined by a point `xyz' and * `dxdy' the extents along the x-, y-axes. */ SCAD_API res_T scad_add_rectangle - (const char* name, /* Can be NULL */ - const double xyz[3], + (const double xyz[3], const double dxdy[2], struct scad_geometry** rectangle); -/* Add a disk in (xy) plane to the scene, defined by a the center `xyz' and +/* Add a disk in the (xy) plane to the scene, defined by its `center' and * `radius'. */ SCAD_API res_T scad_add_disk - (const char* name, /* Can be NULL */ - const double xyz[3], + (const double center[3], const double radius, struct scad_geometry** disk); -/* Add a polygonal surface in (xy) plane to the scene at elevation z */ +/* Add a polygon in the (xy) plane to the scene. + * The `polygon' has `count' vertice and is at elevation `z', the vertice are + * defined by calls to user-provided function `get_position'. */ SCAD_API res_T scad_add_polygon - (const char* name, /* Can be NULL */ - void (*get_position)(const size_t ivert, double pos[2], void* data), + (void (*get_position)(const size_t ivert, double pos[2], void* data), void* data, /* Custom data; can be NULL if get_position don't use it */ const double z, - const size_t count, /* size of x and y arrays */ + const size_t count, struct scad_geometry** polygon); -/* Add a parallelepipedic box to the scene, defined by a point `xyz' and +/* Add a parallelepipedic `box' to the scene, defined by a point `xyz' and * `dxdydz' the extents along the x-, y- and z-axes. */ SCAD_API res_T scad_add_box - (const char* name, /* Can be NULL */ - const double xyz[3], + (const double xyz[3], const double dxdydz[3], struct scad_geometry** box); -/* Add a cylinder to the scene, defined by the center `xyz' of its first - * circular face, the vector `axis' defining its axis and its radius `rad'. The - * `angle' argument defines the angular opening (from 0 to 2*Pi). */ +/* Add a `cylinder' to the scene, defined by the center `center' of its first + * circular face, the vector `axis' defining its axis and its radius `radius'. + * The `angle' argument defines the angular opening (from 0 to 2*PI). */ SCAD_API res_T scad_add_cylinder - (const char* name, /* Can be NULL */ - const double xyz[3], + (const double center[3], const double axis[3], const double radius, const double angle, struct scad_geometry** cylinder); -/* Add a sphere of center `xyz' and radius `rad' to the scene. */ +/* Add a `sphere' of center `center' and radius `radius' to the scene. */ SCAD_API res_T scad_add_sphere - (const char* name, /* Can be NULL */ - const double xyz[3], + (const double center[3], 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). */ +/* Add a `cone' to the scene, defined by the center `center' of its first + * circular face, the vector `axis' defining its axis and its 2 radii `radius1' + * and `radius2'. Note that the 2 radii cannot be identical (one of them can be + * zero). + * The `angle' argument defines the angular opening (from 0 to 2*PI). */ SCAD_API res_T -scad_geometries_equal - (struct scad_geometry* geom1, - struct scad_geometry* geom2, +scad_add_cone + (const double center[3], + const double axis[3], + const double radius1, + const double radius2, + const double angle, + struct scad_geometry** cone); + +/* Add a `torus' to the scene, defined by its `center', the vector `axis' + * defining its axis and its 2 positive radii `radius1' and `radius2'. + * The `angle' argument defines the angular opening (from 0 to 2*PI). + * If `z_axis' is provided, it defines the Z axis of the torus. Otherwise the + * absolute Z axis is used. */ +SCAD_API res_T +scad_add_torus + (const double center[3], + const double radius1, + const double radius2, + const double angle, + const double z_axis[3], /* Can be NULL */ + struct scad_geometry** torus); + +/* Scale the geometry `geometry' by factors `scale' along the three coordinate axes; + * Use `center', as the center of the homothetic transformation. */ +SCAD_API res_T +scad_geometry_dilate + (const struct scad_geometry* geometry, + const double center[3], + const double scale[3], + struct scad_geometry** out_geometry); + +/* Translate the geometry `geometry' along (`dx', `dy', `dz'). */ +SCAD_API res_T +scad_geometry_translate + (const struct scad_geometry* geometry, + const double dxdydz[3], + struct scad_geometry** out_geometry); + +/* Rotate the geometry `geometry' by `angle' radians around the axis of revolution + * defined by the point `pt' and the direction `dir'. */ +SCAD_API res_T +scad_geometry_rotate + (const struct scad_geometry* geometry, + const double pt[3], + const double dir[3], + const double angle, + struct scad_geometry** out_geometry); + +/* Extrude the geometry `geometry' using a translation along (`dx', `dy', `dz'). */ +SCAD_API res_T +scad_geometry_extrude + (const struct scad_geometry* geometry, + const double dxdydz[3], + struct scad_geometry** out_geometry); + +/* Return a list of geometries which form the geometry `geometry'. + * The output geometries are created unnamed. + * Whatever the names, if defined they must be unique. + * 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* geometry, + struct scad_geometry*** out_geometries, + size_t* out_count); + +/* Ref counting of geometries: get a new reference to geometry `geometry'. */ +SCAD_API res_T +scad_geometry_ref_get + (struct scad_geometry* geometry); + +/* Ref counting of geometries: release a reference to geometry `geometry'. */ +SCAD_API res_T +scad_geometry_ref_put + (struct scad_geometry* geometry); + +/* Get the number of components of the geometry `geometry'. */ +SCAD_API res_T +scad_geometry_get_count + (const struct scad_geometry* geometry, + size_t* count); + +/* Check if the geometry `geometry' is empty (has count 0). */ +SCAD_API res_T +scad_geometry_is_empty + (const struct scad_geometry* geometry, + int* empty); + +/* Attach some custom data `data' to geometry `geometry'. + * If provided, release() is called when `geometry' is released or if + * set_custom_data is called again. */ +SCAD_API res_T +scad_geometry_set_custom_data + (struct scad_geometry* geometry, + void (*release) (void* data), /* Can be NULL */ + void* data); /* Can be NULL */ + +/* Get the custom data attached to geometry `geometry'. + * If set_custom_data has not been called before, return NULL. */ +SCAD_API res_T +scad_geometry_get_custom_data + (struct scad_geometry* geometry, + void** data); + +/* Set the name of geometry `geometry'. + * If not NULL, names must be unique. */ +SCAD_API res_T +scad_geometry_set_name + (struct scad_geometry* geometry, + const char* name); /* Can be NULL */ + +/* Set the name of geometries in `geometries'. + * If `prefix_name' is not NULL, the geometries are named <prefix_name>_<rank>, + * <rank> counting from `from_rank'. Otherwise their names are set to NULL. + * If not NULL, names must be unique. */ +SCAD_API res_T +scad_geometries_set_name + (struct scad_geometry** geometries, + const size_t geometries_count, + const char* prefix_name, /* Can be NULL */ + const size_t from_rank); + +/* Get a pointer to `geometry's name, NULL if unnamed. + * Note that this reference is only valid during the lifetime of `geometry' + * (don't use name after deleting `geometry'). */ +SCAD_API res_T +scad_geometry_get_name + (const struct scad_geometry* geometry, + const char** name); + +/* Swap the internals of geometry pools `pool1' and `pool2' + * (i.e. swap internals of pool1[i] and pool2[i]). + * What is swapped is set using `flags'. + * Pools must have the same `count'. */ +SCAD_API res_T +scad_geometries_swap + (struct scad_geometry** pool1, + struct scad_geometry** pool2, + const size_t count, + const int flags); + +/* Get the `mass' of the geometry `geometry'. It means area for a 2D geometry + * and volume for a 3D geometry. */ +SCAD_API res_T +scad_geometry_get_mass + (struct scad_geometry* geometry, + double* mass); + +/* Get the center of mass of geometry `geometry' in `center'. */ +SCAD_API res_T +scad_geometry_get_centerofmass + (struct scad_geometry* geometry, + double center[3]); + +/* Get the `closest' point on geometry `geometry' from point `from'. + * Return the `closest' point and its `distance'. + * The underlying 2D geometry on wich the closest point is located is returned + * as a new geometry in `underlying_geometry' if it is not NULL. + * If `geometry' is 3D, this underlying geometry is (a part of) its boundary. */ +SCAD_API res_T +scad_geometry_get_closest_point + (struct scad_geometry* geometry, + const double from[3], + double closest[3], + double* distance, + struct scad_geometry** underlying_geometry); /* Can be NULL */ + +/* Get the normal of the geometry `geometry' at position `p'. + * The normal is set in `N' and the underlying 2D geometry on which `p' is + * located is returned as a new geometry in `underlying_geometry' if it is not + * NULL. + * If `geometry' is 3D, this underlying geometry is (a part of) its boundary. + * Note that the position `p' is supposed to be close enough of `geometry', or + * this operation is meaningless (as the normal is taken on a computed point on + * the geometry that is the closest point from position `p'). */ +SCAD_API res_T +scad_geometry_get_normal + (struct scad_geometry* geometry, + const double p[3], + double N[3], + struct scad_geometry** underlying_geometry); /* Can be NULL */ + +/* Get the Boundig Box of geometry `geometry' in the form of `min' and `max' + * vectors. */ +SCAD_API res_T +scad_geometry_get_bounding_box + (struct scad_geometry* geometry, + double min[3], + double max[3]); + +/* Check if geometries `geom1' and `geom2' share the same content. + * Note that names are not compared, as they CANNOT be the same. + * Also note that copied geometries (scad_geometry_copy) are not equal, as their + * contents are not shared, but are copies. + * On the other hand, collected content (scad_geometries_collect) is equal to + * its source. + * To check if 2 geometries are copies of one another, one as to apply boolean + * operators (e.g. cut) and check the result accordingly (e.g. empty result). */ +SCAD_API res_T +scad_geometry_equal + (const struct scad_geometry* geom1, + const struct scad_geometry* geom2, int* equal); -/* Check if all the entities of `geometry' are part of the geometries in +/* Check if all the entities of `geometry' are part of one of the geometries in * `geometries'. */ SCAD_API res_T scad_geometry_is_included - (struct scad_geometry* geometry, + (const 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'. */ +/* Create a new geometry `out_geometry' sharing all the content of geometries in + * `geometries'. + * Note that, while copied geometries (scad_geometry_copy) are not equal as + * their internals are copies (not shared content), collected content + * (scad_geometries_collect) is equal to its source. */ SCAD_API res_T -scad_collect_geometries - (const char* name, /* Can be NULL */ - struct scad_geometry** geometries, +scad_geometries_collect + (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 -scad_fuse_geometries - (const char* name, /* Can be NULL */ - struct scad_geometry** geometries, +scad_geometries_fuse + (struct scad_geometry** geometries, const size_t geometries_count, struct scad_geometry** tools, const size_t tools_count, struct scad_geometry** out_geometry); /* Compute the boolean difference between the geometries in `geometries' and - * `tools'. */ + * `tools'. + * Note that the resulting geometry can be empty. */ SCAD_API res_T -scad_cut_geometries - (const char* name, /* Can be NULL */ - struct scad_geometry** geometries, +scad_geometries_cut + (struct scad_geometry** geometries, const size_t geometries_count, struct scad_geometry** tools, const size_t tools_count, struct scad_geometry** out_geometry); /* Compute the boolean intersection (the common parts) of the geometries - * in `geometries' and `tools'. */ + * in `geometries' and `tools'. + * Note that the resulting geometry can be empty. */ SCAD_API res_T -scad_intersect_geometries - (const char* name, /* Can be NULL */ - struct scad_geometry** geometries, - const size_t geometries_count, - struct scad_geometry** tools, - const size_t tools_count, - struct scad_geometry** out_geometry); - -/* compute boundary intersection (the common part) of the geometries in - * `geometries' and `tools'. */ -SCAD_API res_T -scad_geometries_common_boundaries - (const char* name, /* Can be NULL */ - struct scad_geometry** geometries, +scad_geometries_intersect + (struct scad_geometry** geometries, const size_t geometries_count, struct scad_geometry** tools, const size_t tools_count, @@ -400,7 +550,7 @@ scad_geometries_common_boundaries /* Compute the boolean fragments (general fuse) resulting from the * intersection of the geometries in `geometries', making all interfaces * conformal. - * `flags' should be made by Oring values from enum scad_partition_flags to + * `flags' should be made by ORing values from enum scad_partition_flags to * enable non default behaviours (default is to disallow overlapping). * The output geometries are created unnamed. * When applied to geometries of different dimensions, the lower dimensional @@ -413,134 +563,155 @@ scad_geometries_partition const int flags, struct scad_geometry** out_geometries); -/* Get the boundary of the geometries in `geometries'. */ +/* Compute boundaries' intersection (the common part) of the geometries in + * `geometries' and `tools'. + * The output geometries are created unnamed. + * The result `out_boundaries' being allocated using the allocator provided when + * initializing star-cad, it should be freed accordingly. + * If there is no common boundary, `out_boundaries' is set to NULL and + * `out_count' is set to 0. + * Note that there is usually no common boundaries between geometries before + * they have been partitioned. */ SCAD_API res_T -scad_geometry_boundary - (const char* name, /* Can be NULL */ - struct scad_geometry** geometries, +scad_geometries_common_boundaries + (struct scad_geometry** geometries, + const size_t geometries_count, + struct scad_geometry** tools, + const size_t tools_count, + struct scad_geometry*** out_boundaries, + size_t *out_count); + +/* Get the boundaries of the geometries in `geometries', considered as a whole. + * The output geometries are created unnamed. + * The result `out_boundaries' being allocated using the allocator provided when + * initializing star-cad, it should be freed accordingly. */ +SCAD_API res_T +scad_geometries_boundaries + (struct scad_geometry** geometries, const size_t geometries_count, - struct scad_geometry** out_boundary); + struct scad_geometry*** out_boundaries, + size_t *out_count); -/* copy the geometry `geom'. */ +/* Copy the geometry `geometry', except for its name. + * The new geometry remains unnamed. */ SCAD_API res_T scad_geometry_copy - (const struct scad_geometry* geom, - const char* name, /* Can be NULL */ + (const struct scad_geometry* geometry, struct scad_geometry** out_copy); -/* copy the geometry `geom'. */ +/* Change the visibility of the geometry `geometry'. + * If `recursive' is set, constituents of `geometry' are recursively affected down + * to dim 0 (vertices). + * Can be used in conjunction with option MeshOnlyVisible. */ SCAD_API res_T scad_geometry_set_visibility - (const struct scad_geometry* geom, + (const struct scad_geometry* geometry, int visible, int recursive); -/* Change the name of geometry `geom'. */ -SCAD_API res_T -scad_geometry_rename - (struct scad_geometry* geom, - const char* name); /* Can be NULL */ - -/* Scale the geometry by * factors `scale' along the three coordinate axes; - * use `center', as the center of the homothetic transformation. */ +/* Flag `target' geometries as being the result of applying the `affine' + * tranform to `source' geometries. + * The result is that the mesh generated for `target' is the image on the mesh + * generated for `source' through the `affine' transform. + * Only meaningful for surfaces. If called on a volume, it applies to its 2D + * constituents. + * The two sets of surfaces must match topologically (same number of points, + * etc.). */ SCAD_API res_T -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); +scad_geometries_set_periodic + (struct scad_geometry** source, + const size_t source_count, + struct scad_geometry** target, + const size_t target_count, + double affine[16]); -/* Translate the geometry `geom' along (`dx', `dy', `dz'). */ +/* Set a size modifier for geometries in `geometries'. + * When meshing these geometries, triangles' size will be either size*modifier, + * or modifier where size would be the size of the triangle in the absence of a + * size modifier. + * The size modifier is applied recursively down to dimension 0 (points). + * If multiple size modifiers are applied, the order matters as the last applied + * size modifier remains. + * To reset a size modifier, just apply a new Scad_size_factor modifier of 1. */ SCAD_API res_T -scad_geometry_translate - (const struct scad_geometry* geom, - const double dxdydz[3], - const char* name, /* Can be NULL */ - struct scad_geometry** out_geometry); +scad_geometries_set_mesh_size_modifier + (struct scad_geometry** geometries, + const size_t geometries_count, + enum scad_size_modifier_type type, + double modifier); -/* Rotate the geometry `geom' by `angle' radians around the axis of revolution - * defined by the point `pt' and the direction `dir'. */ +/* Set a specific mesh algorithm for geometries in `geometries'. + * Only apply to surfaces (dimension 2). If called on a volume, it applies to + * its 2D constituents. */ SCAD_API res_T -scad_geometry_rotate - (const struct scad_geometry* geom, - const double pt[3], - const double dir[3], - const double angle, - const char* name, /* Can be NULL */ - struct scad_geometry** out_geometry); +scad_geometries_set_mesh_algorithm + (struct scad_geometry** geometries, + const size_t geometries_count, + enum scad_mesh_algorithm algorithm); -/* Extrude the geometry `geom' using a translation along (`dx', `dy', `dz'). */ +/* Clear the mesh of the geometries in `geometries'. + * Note that the mesh of a geometry can only be cleared if it is not on the + * boundary of another geometry with a non-empty mesh. */ SCAD_API res_T -scad_geometry_extrude - (struct scad_geometry* geom, - const char* name, /* Can be NULL */ - const double dxdydz[3], - struct scad_geometry** out_geometry); +scad_geometries_clear_mesh + (struct scad_geometry** geometries, + const size_t geometries_count); -/* Return a list of geometries which form the geometry `geom'. - * 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, - const char* prefix_name, /* Can be NULL */ - struct scad_geometry*** out_geometries, - size_t* out_geometry_n); +/******************************************************************************* + * I/O API + ******************************************************************************/ -/* Import a step model from file `filename'. The imported geometries are - * recorded in `out_geometries'. +/* Import a step model from file `filename'. + * The imported geometries are recorded in `out_geometries'. + * The output geometries are created unnamed. * Note that `out_geometries' is allocated using the allocator provided when * initializing star-cad and should be freed accordingly. */ SCAD_API res_T scad_step_import (const char* filename, /* name of step file */ - const char* name, /* Can be NULL */ struct scad_geometry*** out_geometries, - size_t* out_geometry_n); + size_t* out_count); -/* Export the mesh of geometry `geom' to an STL file. +/* Export the mesh of geometry `geometry' to an STL file. * In order to get a mesh, one has to call scad_scene_mesh before calling this. - * If `file_name' is provided it is used to name the file (just adding .stl), + * If `filename' 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 - * file_name nor the geometry name is defined). Mesh orientation can be forced + * filename 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* file_name, + (struct scad_geometry* geometry, + const char* filename, /* Can be NULL if geometry name is defined */ const enum scad_normals_orientation orientation, - const int binary); /* File format */ + const int binary); /* 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, + struct scad_geometry** exclude, /* Can be NULL */ const size_t exclude_count, - const char* file_name, + const char* filename, /* Can be NULL if geometry name is defined */ const enum scad_normals_orientation orientation, const int binary); -/* 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. */ +/* Export the geometry `geometry' in as many files than its count. + * The files are named <base>_<rank>.stl, where <base> is either `filename_base' + * (first choice) or `geometry's name, <rank> counting from 0. */ SCAD_API res_T scad_stl_export_split - (struct scad_geometry* geom, - const char* file_name, + (struct scad_geometry* geometry, + const char* filename_base, /* Can be NULL if geometry name is defined */ const enum scad_normals_orientation orientation, - const int binary); /* File format */ + const int binary); -/* Accumulate the mesh of the geometry `geom' into `triangles', each triangle +/* Accumulate the mesh of the geometry `geometry' 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 scad_geometry* geometry, struct darray_double* triangles); /* Same as previous, but geometries in `exclude', that can be 2D and/or 3D, are @@ -548,7 +719,7 @@ scad_stl_get_data SCAD_API res_T scad_stl_get_data_partial (struct scad_geometry* geometry, - struct scad_geometry** exclude, + struct scad_geometry** exclude, /* Can be NULL */ const size_t exclude_count, struct darray_double* triangles); @@ -561,76 +732,12 @@ scad_stl_data_write 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); - -/* Create the mesh of the whole scene. */ -SCAD_API res_T -scad_scene_mesh - (void); - -/* Flag `target' geometries as being the result of applying the `affine' - * tranform to `source' geometries. - * The result is that the mesh generated for `target' is the image on the mesh - * generated for `source' through the `affine' transform. - * Only apply to surfaces (dimension 2). If called on a volume, it applies to - * its 2D constituents. - * The two sets of surfaces must match exactly (same number of points, etc.). */ -SCAD_API res_T -scad_geometries_set_periodic - (struct scad_geometry** source, - const size_t source_count, - struct scad_geometry** target, - const size_t target_count, - double affine[16]); -/* Set a size modifier for geometries in `geometries'. - * When meshing these geometries, triangles' size will be either size*modifier, - * or modifier where size would be the size of the triangle in the absence of a - * size modifier. - * The size modifier is applied recursively down to dimension 0 (points). - * If multiple size modifiers are applied, the order matters as the last applied - * size modifier remains. - * To reset a size modifier, just apply a new Scad_size_factor modifier of 1. */ -SCAD_API res_T -scad_geometries_set_mesh_size_modifier - (struct scad_geometry** geometries, - const size_t geometries_count, - enum scad_size_modifier_type type, - double modifier); - -/* Set a specific mesh algorithm for geometries in `geometries'. - * Only apply to surfaces (dimension 2). If called on a volume, it applies to - * its 2D constituents. */ -SCAD_API res_T -scad_geometries_set_mesh_algorithm - (struct scad_geometry** geometries, - const size_t geometries_count, - enum scad_mesh_algorithm algorithm); - -/* Clear the mesh of the geometries in `geometries'. - * Note that the mesh of a geometry can only be cleared if it is not on the - * boundary of another geometry with a non-empty mesh. */ -SCAD_API res_T -scad_geometries_clear_mesh - (struct scad_geometry** geometries, - const size_t geometries_count); - -/* 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, - double p[3], - double N[3], - const char* name, /* Can be NULL */ - struct scad_geometry** out_geometry); - -/* The following API calls are meant for debugging purposes. - * They can be called from gdb. */ +/******************************************************************************* + * Debug API + * 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: @@ -663,19 +770,21 @@ scad_get_dimtag_refcount (const int dim, const int tag); -/* Dump geometry `geom' with address/name, ref count and tags. +/* Dump geometry `geometry' with address/name, ref count and its OCC internal + * dim.tag list. * To use it from gdb: * (gdb) call scad_dump_geometry( <geom_ptr> ) */ SCAD_API res_T scad_dump_geometry - (const struct scad_geometry* geom); + (const struct scad_geometry* geometry); -/* Dump all the geometries with address/name, ref count and tags. +/* Dump all the geometries with address/name, ref count and and their OCC + * internal dim.tag lists. * To use it from gdb: * (gdb) call scad_dump_geometries() */ -SCAD_API void +SCAD_API res_T scad_dump_geometries (void); diff --git a/src/scad_device.c b/src/scad_device.c @@ -30,58 +30,6 @@ /******************************************************************************* * 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) { @@ -97,8 +45,8 @@ device_release(struct scad_device* dev) option = dev->options.Misc.LogRefCounting; empty = htable_geometries_is_empty(&dev->allgeom); log_type = empty ? LOG_OUTPUT : LOG_WARNING; - log = (option & Scad_log_dimTags_all) - || (!empty && (option & Scad_log_dimTags_only_undeleted)); + log = (option & SCAD_LOG_DIMTAGS_ALL) + || (!empty && (option & SCAD_LOG_DIMTAGS_ONLY_UNDELETED)); dev->log = log; dev->log_type = log_type; @@ -113,13 +61,15 @@ device_release(struct scad_device* dev) } while(!htable_geometries_iterator_eq(&it, &end)) { struct scad_geometry* geom = *htable_geometries_iterator_key_get(&it); - SCAD(geometry_ref_put(geom)); + while(geom->ref > 0) { + SCAD(geometry_ref_put(geom)); + } htable_geometries_iterator_next(&it); } htable_names_release(&dev->geometry_names); htable_geometries_release(&dev->allgeom); - device_release_tags_of_dim(dev, 2); - device_release_tags_of_dim(dev, 3); + htable_tags2desc_release(dev->tags2desc); + htable_tags2desc_release(dev->tags2desc+1); htable_size_modifiers_release(&dev->size_modifiers_by_dim[0]); htable_size_modifiers_release(&dev->size_modifiers_by_dim[1]); htable_size_modifiers_release(&dev->size_modifiers_by_dim[2]); @@ -130,6 +80,11 @@ device_release(struct scad_device* dev) MEM_RM(dev->allocator, dev); htable_geometries_release(&tmp); + if(dev->options.Misc.DebugEmptyContext) { + /* After releasing all star-cad stuff, gmsh and OCC contexts must be empty */ + return check_empty_gmsh_occ(dev); + } + return res; } @@ -191,12 +146,29 @@ check_device goto error; } - if(get_device()->need_synchro) { - ERR(scad_synchronize()); + if(g_device->options.Misc.RunUIAtEachStep) { + ERR(scad_run_ui()); + } + + if(g_device->options.Misc.DebugEmptyContext + && htable_geometries_size_get(&g_device->allgeom) == 0) + { + ERR(check_empty_gmsh_occ(g_device)); } - if(g_device->options.Misc.Step) { - ERR(scad_run_ui()); +exit: + return res; +error: + goto exit; +} + +res_T +sync_device(void) +{ + res_T res = RES_OK; + + if(g_device->need_synchro) { + ERR(scad_synchronize()); /* Reset need_synchro according to options */ } exit: @@ -218,7 +190,7 @@ device_register_tags { res_T res = RES_OK; struct scad_device* dev = get_device(); - int log = (dev->options.Misc.LogRefCounting & Scad_log_dimTags_all); + int log = (dev->options.Misc.LogRefCounting & SCAD_LOG_DIMTAGS_ALL); ASSERT(geom); @@ -265,7 +237,6 @@ scad_dump_geometry (const struct scad_geometry* geom) { res_T res = RES_OK; - struct scad_device* dev = get_device(); size_t i; if(!geom) { @@ -274,11 +245,6 @@ scad_dump_geometry } ERR(check_device(FUNC_NAME)); - if(htable_geometries_is_empty(&dev->allgeom)) { - printf("No geometry defined.\n"); - return res; - } - if(str_is_empty(&geom->name)) { printf("Unnamed geometry %p (count is %lu), tags: ", (void*)geom, (long unsigned)geom->ref); @@ -299,28 +265,35 @@ error: goto exit; } -void +res_T scad_dump_geometries (void) { + res_T res = RES_OK; struct scad_device* dev = get_device(); struct htable_geometries_iterator it, end; + size_t cpt = 0; + + ERR(check_device(FUNC_NAME)); - if(!dev) { - printf("Error: star-cad is not initialized.\n"); - return; - } if(htable_geometries_is_empty(&dev->allgeom)) { printf("No geometry defined.\n"); - return; + goto exit; /* Not an error */ } 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); - SCAD(dump_geometry(geom)); + ERR(scad_dump_geometry(geom)); + cpt++; htable_geometries_iterator_next(&it); } + printf("Counted %ld geometries.\n", cpt); + +exit: + return res; +error: + goto exit; } static void @@ -355,7 +328,7 @@ do_device_tags_ref_get res_T res = RES_OK; size_t i; struct scad_device* dev = get_device(); - int log = (dev->options.Misc.LogRefCounting & Scad_log_dimTags_all); + int log = (dev->options.Misc.LogRefCounting & SCAD_LOG_DIMTAGS_ALL); enum log_type log_type = dev->log_type; ASSERT(dimTags || count == 0); @@ -374,6 +347,7 @@ do_device_tags_ref_get struct tag_desc d; tag_desc_init(dev->allocator, &d); ERR(htable_tags2desc_set(t2d, &tag, &d)); + dev->need_synchro = 1; if(log) { logger_print(dev->logger, log_type, "New tag %d.%d (count set to 1).\n", dim, tag); @@ -451,6 +425,7 @@ do_device_tags_ref_put dim, tag); } gmshModelOccRemove(dimTags+i, 2, 0, &ierr); + dev->need_synchro = 1; ERR(gmsh_err_to_res_T(ierr)); break; case Scad_delete_recursive: @@ -459,6 +434,7 @@ do_device_tags_ref_put dim, tag); } gmshModelOccRemove(dimTags+i, 2, 1, &ierr); + dev->need_synchro = 1; ERR(gmsh_err_to_res_T(ierr)); break; default: FATAL("Invalid enum value"); @@ -558,6 +534,78 @@ error: goto exit; } +res_T +check_empty_gmsh_occ(struct scad_device* dev) +{ + int* dimTags = NULL; + size_t dimTags_n, i; + int ierr, found, d; + struct str msg; + int msg_initialized = 0; + res_T res = RES_OK; + + found = 0; + for(d = 3; d >= 0; d--) { + gmshFree(dimTags); + dimTags = NULL; + gmshModelOccGetEntities(&dimTags, &dimTags_n, d, &ierr); + ASSERT(dimTags_n % 2 == 0); + if(dimTags_n == 0) + continue; + found = 1; + log_error(dev, + "There are %ld unreferenced Open-Cascade entities of dim %d from an empty star-cad context%c\n", + dimTags_n / 2, d, (d > 1 ? ':' : '.')); + if(d < 2) continue; + if(!msg_initialized) str_init(dev->allocator, &msg); + for(i = 0; i < dimTags_n; i += 2) { + const int dim = dimTags[i]; + const int tag = dimTags[i+1]; + ERR(str_append_printf(&msg, (i ? ", %d.%d" : "%d.%d"), dim, tag)); + } + log_error(dev," tags [%s]\n", str_cget(&msg)); + } + if(found) { + res = RES_BAD_ARG; + goto error; + } + + ERR(sync_device()); + + found = 0; + for(d = 3; d >= 0; d--) { + gmshFree(dimTags); + dimTags = NULL; + gmshModelGetEntities(&dimTags, &dimTags_n, d, &ierr); + ASSERT(dimTags_n % 2 == 0); + if(dimTags_n == 0) + continue; + found = 1; + log_error(dev, + "There are %ld unreferenced gmsh entities of dim %d from an empty star-cad context%c\n", + dimTags_n / 2, d, (d > 1 ? ':' : '.')); + if(d < 2) continue; + if(!msg_initialized) str_init(dev->allocator, &msg); + for(i = 0; i < dimTags_n; i += 2) { + const int dim = dimTags[i]; + const int tag = dimTags[i+1]; + ERR(str_append_printf(&msg, (i ? ", %d.%d" : "%d.%d"), dim, tag)); + } + log_error(dev," tags [%s]\n", str_cget(&msg)); + } + if(found) { + res = RES_BAD_ARG; + goto error; + } + +exit: + gmshFree(dimTags); + if(msg_initialized) str_release(&msg); + return res; +error: + goto exit; +} + /******************************************************************************* * API scad_device functions ******************************************************************************/ @@ -594,10 +642,10 @@ scad_initialize } g_device->logger = logger ? logger : LOGGER_DEFAULT; g_device->allocator = allocator; - g_device->need_synchro = g_device->options.Misc.DebugOpenCascadeSync; + g_device->need_synchro = g_device->options.Misc.DebugAutoSync; g_device->verbose = verbose; g_device->log_type = LOG_OUTPUT; - g_device->log = (g_device->options.Misc.LogRefCounting != Scad_log_none); + 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_tags2desc_init(allocator, &g_device->tags2desc[0]); @@ -635,8 +683,8 @@ scad_finalize empty = htable_geometries_is_empty(&dev->allgeom); log_type = empty ? LOG_OUTPUT : LOG_WARNING; - log = (option & Scad_log_dimTags_all) - || (!empty && (option & Scad_log_dimTags_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"); @@ -706,17 +754,18 @@ scad_set_options if(options) { /* Check non-gmsh option validity if user-provided */ - (void)actual_options->Misc.Step; /* int boolean: always OK */ + (void)actual_options->Misc.RunUIAtEachStep; /* int boolean: always OK */ (void)actual_options->Misc.SynchronizeOnRunUI; /* int boolean: always OK */ (void)actual_options->Misc.LogRefCounting; /* int boolean: always OK */ - (void)actual_options->Misc.DebugOpenCascadeSync; /* int boolean: always OK */ + (void)actual_options->Misc.DebugAutoSync; /* int boolean: always OK */ + (void)actual_options->Misc.DebugEmptyContext; /* int boolean: always OK */ } dev->options = *actual_options; /* Update logging policy */ dev->log - = (dev->options.Misc.LogRefCounting & Scad_log_dimTags_all); + = (dev->options.Misc.LogRefCounting & SCAD_LOG_DIMTAGS_ALL); exit: return res; diff --git a/src/scad_device.h b/src/scad_device.h @@ -56,7 +56,9 @@ hash_str(const struct str* a) #include <rsys/hash_table.h> enum delete_policy { + /* Delete the tag and all its lower-dimension components */ Scad_delete_recursive, + /* Delete only the tag, not its lower-dimension components */ Scad_delete_non_recursive, Scad_do_not_delete }; @@ -196,6 +198,9 @@ LOCAL_SYM res_T check_device (const char* function_name); +LOCAL_SYM res_T +sync_device(void); + LOCAL_SYM struct scad_device* get_device (void); @@ -234,4 +239,8 @@ device_unregister_tags const enum log_type log_type, struct scad_geometry* geom); +LOCAL_SYM res_T +check_empty_gmsh_occ + (struct scad_device* dev); + #endif diff --git a/src/scad_geometry.c b/src/scad_geometry.c @@ -22,6 +22,7 @@ #include <rsys/mem_allocator.h> #include <rsys/str.h> #include <rsys/math.h> +#include <rsys/double2.h> #include <rsys/double3.h> #include <rsys/logger.h> #include <rsys/hash_table.h> @@ -55,51 +56,50 @@ struct coord_pair { static res_T geom_set_name (struct scad_geometry* geom, - const char* name) + const struct str* name) /* Can be NULL but not "" */ { struct scad_device* dev = get_device(); - struct mem_allocator* allocator = dev->allocator; - struct str str_name; - int name_initialized = 0; - int same_name; + int same_name, name_isnt_null, previous_name_is_null; res_T res = RES_OK; ASSERT(geom); + ASSERT(!name || !str_is_empty(name)); + previous_name_is_null = str_is_empty(&geom->name); if(name) { - if(strlen(name) == 0) { - res = RES_BAD_ARG; - log_error(get_device(), "Geometry name \"\" is invalid.\n"); - goto error; - } - str_init(allocator, &str_name); - name_initialized = 1; - ERR(str_set(&str_name, name)); - if(htable_names_find(&dev->geometry_names, &str_name)) { - /* if defined, names must be unique */ + name_isnt_null = 1; + same_name = (!previous_name_is_null) && (0 == str_cmp(name, &geom->name)); + } else { + name_isnt_null = 0; + same_name = previous_name_is_null; + } + + if(same_name) /* No change needed */ + goto exit; + + if(name_isnt_null) { + struct scad_geometry** g + = htable_names_find(&dev->geometry_names, name); + if(g) { /* if defined, names must be unique */ res = RES_BAD_ARG; log_error(get_device(), "Geometry name '%s' is allready in use.\n", - name); + str_cget(name)); goto error; } } - same_name = (!name && str_is_empty(&geom->name)) - || (name && 0 == strcmp(name, str_cget(&geom->name))); - - if(!same_name) { + if(!previous_name_is_null) { 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)); + ASSERT((n == 1)); (void)n; } - if(name) { - ERR(htable_names_set(&dev->geometry_names, &geom->name, &geom)); + if(name_isnt_null) { + ERR(str_copy(&geom->name, name)); + ERR(htable_names_set(&dev->geometry_names, name, &geom)); } else { str_clear(&geom->name); } exit: - if(name_initialized) str_release(&str_name); return res; error: goto exit; @@ -107,8 +107,7 @@ error: static int mixed_dim_err_msg - (const char* name, /* Can be NULL */ - const char* op, + (const char* op, struct scad_geometry** geometries, const size_t geometries_count, int* dim) @@ -122,8 +121,7 @@ mixed_dim_err_msg if(*dim == INT_MAX) *dim = d; else if (*dim != d) { log_error(dev, - "Dimension mismatch in %s operation creating '%s' geometry.\n", - op, (name ? name : "unnamed")); + "Dimension mismatch in %s operation creating geometry.\n", op); return 1; } } @@ -133,8 +131,7 @@ mixed_dim_err_msg static res_T geometry_create - (const char* name, - struct scad_geometry** out_geometry) + (struct scad_geometry** out_geometry) { res_T res = RES_OK; struct scad_geometry* geom = NULL; @@ -153,10 +150,8 @@ geometry_create ref_init(&geom->ref); str_init(allocator, &geom->name); ERR(htable_geometries_set(&dev->allgeom, &geom, &one)); - ERR(geom_set_name(geom, name)); - dev->need_synchro = 1; - if(dev->options.Misc.LogRefCounting & Scad_log_geometry) { + 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); @@ -178,6 +173,123 @@ error: goto end; } +enum tag_operation { + COMMON_TAGS, + UNIQUE_TAGS +}; + +static res_T +process_tag_list + (const int* dt1, + const size_t c1, + const int* dt2, + const size_t c2, + const enum tag_operation op, + int** out_dimTags, + size_t* out_dimTags_n) +{ + res_T res = RES_OK; + size_t i, d, c = 0, sz = 0; + struct scad_device* dev = get_device(); + struct mem_allocator* allocator = dev->allocator; + struct htable_tags t2, t3; + struct htable_tags_iterator it, end; + int* dimTags = NULL; + const char set1 = BIT(0), set2 = BIT(1), both = set1 | set2; + + ASSERT((dt1 || c1 == 0) && (dt2 || c2 == 0) && out_dimTags && out_dimTags_n); + + htable_tags_init(allocator, &t2); + htable_tags_init(allocator, &t3); + + /* list tags and flags them whith set(s) they are from */ + for(i = 0; i < c1; i += 2) { + int dim = dt1[i]; + int tag = dt1[i+1]; + struct htable_tags* tn = (dim == 2) ? &t2 : &t3; + ASSERT(dim == 2 || dim == 3); + ERR(htable_tags_set(tn, &tag, &set1)); + } + for(i = 0; i < c2; i += 2) { + char *fl, tmp; + int dim = dt2[i]; + int tag = dt2[i+1]; + struct htable_tags* tn = (dim == 2) ? &t2 : &t3; + ASSERT(dim == 2 || dim == 3); + fl = htable_tags_find(tn, &tag); + if(fl == NULL) { + tmp = set2; + fl = &tmp; + } else { + *fl |= set2; + } + ERR(htable_tags_set(tn, &tag, fl)); + } + + switch(op) { + case COMMON_TAGS: + for(d = 2; d <= 3; d++) { + struct htable_tags* tn = (d == 2) ? &t2 : &t3; + htable_tags_begin(tn, &it); + htable_tags_end(tn, &end); + while(!htable_tags_iterator_eq(&it, &end)) { + if(*htable_tags_iterator_data_get(&it) == both) sz++; + htable_tags_iterator_next(&it); + } + } + break; + case UNIQUE_TAGS: + sz = htable_tags_size_get(&t2) + htable_tags_size_get(&t3); + break; + default: FATAL("Invalid enum value"); + } + + /* Build result */ + if(sz > 0) { + dimTags = MEM_ALLOC(allocator, sz * 2 * sizeof(*dimTags)); + if(!dimTags) { + res = RES_MEM_ERR; + goto error; + } + + for(d = 2; d <= 3; d++) { + struct htable_tags* tn = (d == 2) ? &t2 : &t3; + htable_tags_begin(tn, &it); + htable_tags_end(tn, &end); + while(!htable_tags_iterator_eq(&it, &end)) { + int add; + switch(op) { + case COMMON_TAGS: + add = (*htable_tags_iterator_data_get(&it) == both); + break; + case UNIQUE_TAGS: + add = 1; + break; + default: FATAL("Invalid enum value"); + } + if(add) { + dimTags[c++] = 2; + dimTags[c++] = *htable_tags_iterator_key_get(&it); + } + htable_tags_iterator_next(&it); + } + } + ASSERT(sz*2 == c); + } + +exit: + *out_dimTags_n = c; + *out_dimTags = dimTags; + htable_tags_release(&t2); + htable_tags_release(&t3); + return res; +error: + MEM_RM(allocator, dimTags); + dimTags = NULL; + c = 0; + goto exit; +} + static res_T gather_tags (struct scad_geometry** geometries, @@ -276,6 +388,8 @@ error: goto exit; } +/* Recursivelly get the tags of geometries, possibly down to dim 0. + * Trigger a call to sync_device() */ static res_T gather_tags_recursive (struct scad_geometry** geometries, @@ -312,6 +426,7 @@ gather_tags_recursive /* Recursively build result by dimension and list constituents, * begining with dim==3 */ + ERR(sync_device()); for(dim = 3; dim >= down_to_dim; dim--) { size_t c = 0; sz[dim] = 2 * htable_tags_size_get(t+dim); @@ -404,11 +519,10 @@ geometry_release(ref_T* ref) size_t n; ASSERT(ref); - dev->need_synchro = 1; geom = CONTAINER_OF(ref, struct scad_geometry, ref); CHK(RES_OK == device_unregister_tags(dev->log, dev->log_type, geom)); MEM_RM(allocator, geom->gmsh_dimTags); - if(str_len(&geom->name) != 0) { + if(!str_is_empty(&geom->name)) { n = htable_names_erase(&dev->geometry_names, &geom->name); ASSERT(n == 1); } @@ -435,7 +549,7 @@ scad_geometry_ref_get ERR(check_device(FUNC_NAME)); ref_get(&geom->ref); - if(dev->options.Misc.LogRefCounting & Scad_log_geometry) { + 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", @@ -463,7 +577,7 @@ scad_geometry_ref_put if(!geom) return RES_BAD_ARG; ERR(check_device(FUNC_NAME)); - if(dev->options.Misc.LogRefCounting & Scad_log_geometry) { + 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", @@ -483,6 +597,28 @@ error: } res_T +scad_scene_count + (size_t* count) +{ + res_T res = RES_OK; + struct scad_device* dev = get_device(); + + if(!count) { + res =RES_BAD_ARG; + goto error; + } + + ERR(check_device(FUNC_NAME)); + + *count = htable_geometries_size_get(&dev->allgeom); + +end: + return res; +error: + goto end; +} + +res_T scad_scene_clear (void) { @@ -506,8 +642,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_dimTags_all) - || (!empty && (option & Scad_log_dimTags_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) { @@ -518,7 +654,9 @@ scad_scene_clear } while(!htable_geometries_iterator_eq(&it, &end)) { struct scad_geometry* geom = *htable_geometries_iterator_key_get(&it); - ERR(scad_geometry_ref_put(geom)); + while(geom->ref > 0) { + ERR(scad_geometry_ref_put(geom)); + } htable_geometries_iterator_next(&it); } if(dev->log) { @@ -562,6 +700,28 @@ error: } res_T +scad_geometry_is_empty + (const struct scad_geometry* geom, + int* empty) +{ + res_T res = RES_OK; + + if(!geom || !empty) { + res = RES_BAD_ARG; + goto error; + } + + ERR(check_device(FUNC_NAME)); + + *empty = (geom->gmsh_dimTags_n == 0); + +exit: + return res; +error: + goto exit; +} + +res_T scad_geometry_set_custom_data (struct scad_geometry* geom, void (*release) (void* data), @@ -631,11 +791,10 @@ scad_geometry_set_visibility data = geom->gmsh_dimTags; sz = geom->gmsh_dimTags_n; + ERR(sync_device()); gmshModelSetVisibility(data, sz, visible, recursive, &ierr); ERR(gmsh_err_to_res_T(ierr)); - get_device()->need_synchro = 1; - exit: return res; error: @@ -648,28 +807,34 @@ scad_geometries_clear_mesh const size_t geometries_count) { res_T res = RES_OK; - int ierr; - size_t sz; - int* data = NULL; + int ierr, dim; struct scad_device* dev = get_device(); struct mem_allocator* allocator = NULL; + size_t sz[4]; + int *data[4] = { NULL, NULL, NULL, NULL}; - if(!geometries) { + if(!geometries || geometries_count == 0) { res = RES_BAD_ARG; goto error; } ERR(check_device(FUNC_NAME)); - allocator = dev->allocator; - ERR(gather_tags(geometries, geometries_count, &data, &sz)); - gmshModelMeshClear(data, sz, &ierr); - ERR(gmsh_err_to_res_T(ierr)); - dev->need_synchro = 1; + ERR(gather_tags_recursive(geometries, geometries_count, 0, data, sz)); + for(dim = 3; dim >= 0; dim--) { + /* Cannot clear the mesh of lower dim entities if linked to higher dim + * entities with uncleared mesh: start from the higher dim down to 0 */ + gmshModelMeshClear(data[dim], sz[dim], &ierr); + ERR(gmsh_err_to_res_T(ierr)); + } exit: - if(allocator) MEM_RM(allocator, data); + if(allocator) { + for(dim = 3; dim >= 0; dim--) { + MEM_RM(allocator, data[dim]); + } + } return res; error: goto exit; @@ -689,7 +854,11 @@ scad_geometry_get_name ERR(check_device(FUNC_NAME)); - *name = str_cget(&geom->name); + if(str_is_empty(&geom->name)) { + *name = NULL; + } else { + *name = str_cget(&geom->name); + } exit: return res; @@ -707,8 +876,12 @@ scad_geometry_get_mass size_t i; int* data = NULL; size_t sz = 0; + double m = 0; - if(!geom || !mass) goto error; + if(!geom || !mass) { + res = RES_BAD_ARG; + goto error; + } ERR(check_device(FUNC_NAME)); @@ -716,16 +889,16 @@ scad_geometry_get_mass sz = geom->gmsh_dimTags_n; ref_dim = data[0]; - *mass = 0; for(i = 0; i < sz; i += 2) { - double geom_mass = 0; + double geom_mass; int dim = data[i], tag = data[i+1]; int ierr = 0; if(ref_dim != dim) goto error; gmshModelOccGetMass(dim, tag, &geom_mass, &ierr); ERR(gmsh_err_to_res_T(ierr)); - *mass += geom_mass; + m += geom_mass; } + *mass = m; exit: return res; @@ -736,28 +909,42 @@ error: res_T scad_geometry_get_closest_point (struct scad_geometry* geom, - const double* from, - double* closest, - double* closest_distance) + const double from[3], + double closest[3], + double* closest_distance, + struct scad_geometry** out_geometry) { res_T res = RES_OK; size_t i = 0; double* coord = NULL; double* pcoord = NULL; double tmp[3], min[3], min_d = DBL_MAX; + struct scad_device* dev = get_device(); + struct darray_int tags; + const int* data = NULL; + size_t sz = 0; + int initialized = 0, min_tag = -1; + struct scad_geometry* out = NULL; - if(!geom || !from || !closest) { + if(!geom || !from || !closest || !closest_distance) { res = RES_BAD_ARG; goto error; } ERR(check_device(FUNC_NAME)); - for(i = 0; i < geom->gmsh_dimTags_n; i += 2) { + darray_int_init(dev->allocator, &tags); + initialized = 1; + + ERR(get_2d_tags(geom, &tags)); + data = darray_int_cdata_get(&tags); + sz = darray_int_size_get(&tags); + + for(i = 0; i < sz; i++) { double d; int ierr = 0; - int dim = geom->gmsh_dimTags[i]; - int tag = geom->gmsh_dimTags[i + 1]; + int dim = 2; + int tag = data[i]; size_t pcoord_n; size_t coord_n; @@ -768,31 +955,55 @@ scad_geometry_get_closest_point ASSERT(coord_n == 3); d = d3_len(d3_sub(tmp, from, coord)); if(d < min_d) { + min_tag = tag; min_d = d; - d3_set(min, tmp); + d3_set(min, coord); } gmshFree(coord); gmshFree(pcoord); coord = pcoord = NULL; } + ASSERT(min_tag != -1); d3_set(closest, min); *closest_distance = min_d; + if(out_geometry) { + ERR(geometry_create(&out)); + out->gmsh_dimTags = + MEM_ALLOC(dev->allocator, 2 * sizeof(*out->gmsh_dimTags)); + if(!out->gmsh_dimTags) { + res = RES_MEM_ERR; + goto error; + } + out->gmsh_dimTags_n = 2; + out->gmsh_dimTags[0] = 2; + out->gmsh_dimTags[1] = min_tag; + ERR(device_register_tags(out)); + } exit: - gmshFree(coord); - gmshFree(pcoord); + if(initialized) darray_int_release(&tags); + if(out_geometry) *out_geometry = out; + gmshFree(coord); + gmshFree(pcoord); return res; error: + if(out) { + SCAD(geometry_ref_put(out)); + out = NULL; + } goto exit; } res_T scad_geometry_get_centerofmass (struct scad_geometry* geom, - double* center) + double center[3]) { res_T res = RES_OK; - size_t i = 0; + double *centers = NULL, *masses = NULL; + size_t i = 0, count; + struct mem_allocator* allocator; + int ierr; if(!geom || !center) { res = RES_BAD_ARG; @@ -801,19 +1012,39 @@ scad_geometry_get_centerofmass ERR(check_device(FUNC_NAME)); - for(i = 0; i < geom->gmsh_dimTags_n; i += 2) { - double x, y, z; - int ierr = 0; + ASSERT(geom->gmsh_dimTags_n % 2 == 0); + count = geom->gmsh_dimTags_n / 2; + if(count == 1) { int dim = geom->gmsh_dimTags[i]; int tag = geom->gmsh_dimTags[i + 1]; - gmshModelOccGetCenterOfMass(dim, tag, &x, &y, &z, &ierr); + gmshModelOccGetCenterOfMass(dim, tag, center, center+1, center+2, &ierr); ERR(gmsh_err_to_res_T(ierr)); - center[3*i] = x; - center[3*i + 1] = y; - center[3*i + 2] = z; + } else { + double c[3] = { 0, 0, 0 }, tmp[3], m = 0; + allocator = get_device()->allocator; + centers = MEM_ALLOC(allocator, count * 3 * sizeof(*centers)); + masses = MEM_ALLOC(allocator, count * sizeof(*masses)); + if(!centers || !masses) { + res = RES_MEM_ERR; + goto error; + } + for(i = 0; i < count; i++) { + int dim = geom->gmsh_dimTags[2*i]; + int tag = geom->gmsh_dimTags[2*i + 1]; + gmshModelOccGetCenterOfMass(dim, tag, + centers+3*i, centers+3*i+1, centers+3*i+2, &ierr); + ERR(gmsh_err_to_res_T(ierr)); + gmshModelOccGetMass(dim, tag, masses+i, &ierr); + ERR(gmsh_err_to_res_T(ierr)); + d3_add(c, c, d3_muld(tmp, centers+3*i, masses[i])); + m += masses[i]; + } + d3_muld(center, c, 1/m); } exit: + if(centers) MEM_RM(allocator, centers); + if(masses) MEM_RM(allocator, masses); return res; error: goto exit; @@ -859,8 +1090,7 @@ error: res_T scad_add_rectangle - (const char* name, - const double xyz[3], + (const double xyz[3], const double dxdy[2], struct scad_geometry** out_geometry) { @@ -881,7 +1111,7 @@ scad_add_rectangle gmsh_ID = gmshModelOccAddRectangle(SPLIT3(xyz), SPLIT2(dxdy), -1, 0, &ierr); ERR(gmsh_err_to_res_T(ierr)); - ERR(geometry_create(name, &geom)); + ERR(geometry_create(&geom)); geom->gmsh_dimTags_n = 2; geom->gmsh_dimTags = MEM_ALLOC(allocator, geom->gmsh_dimTags_n * sizeof(*geom->gmsh_dimTags)); @@ -907,8 +1137,7 @@ error: res_T scad_add_disk - (const char* name, - const double xyz[3], + (const double xyz[3], const double radius, struct scad_geometry** out_geometry) { @@ -930,7 +1159,7 @@ scad_add_disk NULL, 0, &ierr); ERR(gmsh_err_to_res_T(ierr)); - ERR(geometry_create(name, &geom)); + ERR(geometry_create(&geom)); geom->gmsh_dimTags_n = 2; geom->gmsh_dimTags = MEM_ALLOC(allocator, geom->gmsh_dimTags_n * sizeof(*geom->gmsh_dimTags)); @@ -956,8 +1185,7 @@ error: res_T scad_add_polygon - (const char* name, - void (*get_position)(const size_t ivert, double pos[2], void* data), + (void (*get_position)(const size_t ivert, double pos[2], void* data), void* data, const double z, const size_t count, @@ -1007,7 +1235,7 @@ scad_add_polygon gmsh_ID = gmshModelOccAddPlaneSurface(&loop, 1, -1, &ierr); ERR(gmsh_err_to_res_T(ierr)); - ERR(geometry_create(name, &geom)); + ERR(geometry_create(&geom)); geom->gmsh_dimTags_n = 2; geom->gmsh_dimTags = MEM_ALLOC(allocator, 2 * sizeof(*geom->gmsh_dimTags)); if(!geom->gmsh_dimTags) { @@ -1036,8 +1264,7 @@ error: res_T scad_add_box - (const char* name, - const double xyz[3], + (const double xyz[3], const double dxdydz[3], struct scad_geometry** out_geometry) { @@ -1058,7 +1285,7 @@ scad_add_box gmsh_ID = gmshModelOccAddBox(SPLIT3(xyz), SPLIT3(dxdydz), -1, &ierr); ERR(gmsh_err_to_res_T(ierr)); - ERR(geometry_create(name, &geom)); + ERR(geometry_create(&geom)); geom->gmsh_dimTags_n = 2; geom->gmsh_dimTags = MEM_ALLOC(allocator, 2 * sizeof(*geom->gmsh_dimTags)); if(!geom->gmsh_dimTags) { @@ -1083,8 +1310,7 @@ error: res_T scad_add_cylinder - (const char* name, - const double xyz[3], + (const double xyz[3], const double axis[3], const double radius, const double angle, @@ -1108,7 +1334,7 @@ scad_add_cylinder angle, &ierr); ERR(gmsh_err_to_res_T(ierr)); - ERR(geometry_create(name, &geom)); + ERR(geometry_create(&geom)); geom->gmsh_dimTags_n = 2; geom->gmsh_dimTags = MEM_ALLOC(allocator, 2 * sizeof(*geom->gmsh_dimTags)); if(!geom->gmsh_dimTags) { @@ -1133,8 +1359,7 @@ error: int scad_add_sphere - (const char* name, - const double xyz[3], + (const double xyz[3], const double radius, struct scad_geometry** out_geometry) { @@ -1156,7 +1381,7 @@ scad_add_sphere gmshModelOccAddSphere(SPLIT3(xyz), radius, -1, -PI/2, PI/2, 2*PI, &ierr); ERR(gmsh_err_to_res_T(ierr)); - ERR(geometry_create(name, &geom)); + ERR(geometry_create(&geom)); geom->gmsh_dimTags_n = 2; geom->gmsh_dimTags = MEM_ALLOC(allocator, 2 * sizeof(*geom->gmsh_dimTags)); if(!geom->gmsh_dimTags) { @@ -1180,9 +1405,115 @@ error: } SCAD_API res_T -scad_geometries_equal - (struct scad_geometry* geom1, - struct scad_geometry* geom2, +scad_add_cone + (const double center[3], + const double axis[3], + const double radius1, + const double radius2, + const double angle, + struct scad_geometry** cone) +{ + res_T res = RES_OK; + int ierr, gmsh_ID; + struct scad_geometry* geom = NULL; + struct scad_device* dev = get_device(); + struct mem_allocator* allocator = NULL; + + if(!center || !axis || radius1 < 0 || radius2 < 0 || (radius1 == radius2) + || angle < 0 || angle > 2*PI || !cone) + { + res = RES_BAD_ARG; + goto error; + } + + ERR(check_device(FUNC_NAME)); + allocator = dev->allocator; + + gmsh_ID = + gmshModelOccAddCone(SPLIT3(center), SPLIT3(axis), radius1, radius2, + -1, 2*PI, &ierr); + ERR(gmsh_err_to_res_T(ierr)); + + ERR(geometry_create(&geom)); + geom->gmsh_dimTags_n = 2; + geom->gmsh_dimTags = MEM_ALLOC(allocator, 2 * sizeof(*geom->gmsh_dimTags)); + if(!geom->gmsh_dimTags) { + res = RES_MEM_ERR; + goto error; + } + geom->gmsh_dimTags[0] = 3; + geom->gmsh_dimTags[1] = gmsh_ID; + + ERR(device_register_tags(geom)); + +exit: + if(cone) *cone = geom; + return res; +error: + if(geom) { + SCAD(geometry_ref_put(geom)); + geom = NULL; + } + goto exit; +} + +SCAD_API res_T +scad_add_torus + (const double center[3], + const double radius1, + const double radius2, + const double angle, + const double z_axis[3], /* Can be NULL */ + struct scad_geometry** torus) +{ + res_T res = RES_OK; + int ierr, gmsh_ID; + struct scad_geometry* geom = NULL; + struct scad_device* dev = get_device(); + struct mem_allocator* allocator = NULL; + + if(!center || radius1 <= 0 || radius2 <= 0 || angle < 0 || angle > 2*PI + || !torus) + { + res = RES_BAD_ARG; + goto error; + } + + ERR(check_device(FUNC_NAME)); + allocator = dev->allocator; + + gmsh_ID = + gmshModelOccAddTorus(SPLIT3(center), radius1, radius2, + -1, 2*PI, z_axis, (z_axis == NULL ? 0 : 3), &ierr); + ERR(gmsh_err_to_res_T(ierr)); + + ERR(geometry_create(&geom)); + geom->gmsh_dimTags_n = 2; + geom->gmsh_dimTags = MEM_ALLOC(allocator, 2 * sizeof(*geom->gmsh_dimTags)); + if(!geom->gmsh_dimTags) { + res = RES_MEM_ERR; + goto error; + } + geom->gmsh_dimTags[0] = 3; + geom->gmsh_dimTags[1] = gmsh_ID; + + ERR(device_register_tags(geom)); + +exit: + if(torus) *torus = geom; + return res; +error: + if(geom) { + SCAD(geometry_ref_put(geom)); + geom = NULL; + } + goto exit; +} + +SCAD_API res_T +scad_geometry_equal + (const struct scad_geometry* geom1, + const struct scad_geometry* geom2, int* equal) { res_T res = RES_OK; @@ -1248,7 +1579,7 @@ error: SCAD_API res_T scad_geometry_is_included - (struct scad_geometry* geometry, + (const struct scad_geometry* geometry, struct scad_geometry** geometries, const size_t geometries_count, int* included) @@ -1260,7 +1591,7 @@ scad_geometry_is_included int initialized = 0; size_t i, n; - if(!geometry || !geometries || !included) { + if(!geometry || !geometries || geometries_count == 0 || !included) { res = RES_BAD_ARG; goto error; } @@ -1318,9 +1649,8 @@ error: } res_T -scad_fuse_geometries - (const char* name, - struct scad_geometry** geometries, +scad_geometries_fuse + (struct scad_geometry** geometries, const size_t geometries_count, struct scad_geometry** tools, const size_t tools_count, @@ -1337,6 +1667,10 @@ scad_fuse_geometries struct scad_geometry* geom = NULL; struct scad_device* dev = get_device(); struct mem_allocator* allocator = NULL; +#ifdef FIX_GMSH_BOOLEAN_OUTPUTS + int *tmp1 = NULL, *tmp2 = NULL; + size_t c1, c2; +#endif if(!geometries || !geometries_count || !tools || !tools_count || !out_geometry) { res = RES_BAD_ARG; @@ -1349,13 +1683,26 @@ scad_fuse_geometries ERR(gather_tags(geometries, geometries_count, &data1, &sz1)); ERR(gather_tags(tools, tools_count, &data2, &sz2)); +#ifdef FIX_GMSH_BOOLEAN_OUTPUTS + gmshModelOccCopy(data1, sz1, &tmp1, &c1, &ierr); + ERR(gmsh_err_to_res_T(ierr)); + gmshModelOccCopy(data2, sz2, &tmp2, &c2, &ierr); + ERR(gmsh_err_to_res_T(ierr)); + /* We do remove gmsh objects here as they are temporary copies created to + * allow the remove flag to be used (gmsh is broken here if remove=0) */ + gmshModelOccFuse(tmp1, c1, tmp2, c2, &tagout, &tagoutn, &map, &mapn, &mapnn, + -1, 1, 1, &ierr); +#else /* We don't remove gmsh objects here; they are only removed when their tags are * no longer used by any star-cad geometry */ gmshModelOccFuse(data1, sz1, data2, sz2, &tagout, &tagoutn, &map, &mapn, &mapnn, -1, 0, 0, &ierr); +#endif + + ASSERT(tagoutn % 2 == 0); ERR(gmsh_err_to_res_T(ierr)); - ERR(geometry_create(name, &geom)); + ERR(geometry_create(&geom)); geom->gmsh_dimTags_n = tagoutn; geom->gmsh_dimTags = MEM_ALLOC(allocator, tagoutn * sizeof(*tagout)); if(!geom->gmsh_dimTags) { @@ -1367,6 +1714,10 @@ scad_fuse_geometries ERR(device_register_tags(geom)); exit: +#ifdef FIX_GMSH_BOOLEAN_OUTPUTS + gmshFree(tmp1); + gmshFree(tmp2); +#endif if(out_geometry) *out_geometry = geom; if(allocator) { MEM_RM(allocator, data1); @@ -1386,9 +1737,8 @@ error: } res_T -scad_collect_geometries - (const char* name, - struct scad_geometry** geometries, +scad_geometries_collect + (struct scad_geometry** geometries, const size_t geometries_count, struct scad_geometry** out_geometry) { @@ -1406,7 +1756,7 @@ scad_collect_geometries ERR(gather_tags(geometries, geometries_count, &data, &sz)); - ERR(geometry_create(name, &geom)); + ERR(geometry_create(&geom)); geom->gmsh_dimTags_n = sz; geom->gmsh_dimTags = data; @@ -1424,9 +1774,8 @@ error: } res_T -scad_cut_geometries - (const char* name, /* Can be NULL */ - struct scad_geometry** geometries, +scad_geometries_cut + (struct scad_geometry** geometries, const size_t geometries_count, struct scad_geometry** tools, const size_t tools_count, @@ -1443,6 +1792,10 @@ scad_cut_geometries struct scad_geometry* geom = NULL; struct scad_device* dev = get_device(); struct mem_allocator* allocator = NULL; +#ifdef FIX_GMSH_BOOLEAN_OUTPUTS + int *tmp1 = NULL, *tmp2 = NULL; + size_t c1, c2; +#endif if(!geometries || !geometries_count || !tools || !tools_count || !out_geometry) { res = RES_BAD_ARG; @@ -1455,13 +1808,26 @@ scad_cut_geometries ERR(gather_tags(geometries, geometries_count, &data1, &sz1)); ERR(gather_tags(tools, tools_count, &data2, &sz2)); +#ifdef FIX_GMSH_BOOLEAN_OUTPUTS + gmshModelOccCopy(data1, sz1, &tmp1, &c1, &ierr); + ERR(gmsh_err_to_res_T(ierr)); + gmshModelOccCopy(data2, sz2, &tmp2, &c2, &ierr); + ERR(gmsh_err_to_res_T(ierr)); + /* We do remove gmsh objects here as they are temporary copies created to + * allow the remove flag to be used (gmsh is broken here if remove=0) */ + gmshModelOccCut(tmp1, c1, tmp2, c2, &tagout, &tagoutn, &map, &mapn, + &mapnn, -1, 1, 1, &ierr); +#else /* We don't remove gmsh objects here; they are only removed when their tags are * no longer used by any star-cad geometry */ gmshModelOccCut(data1, sz1, data2, sz2, &tagout, &tagoutn, &map, &mapn, &mapnn, -1, 0, 0, &ierr); +#endif + + ASSERT(tagoutn % 2 == 0); ERR(gmsh_err_to_res_T(ierr)); - ERR(geometry_create(name, &geom)); + ERR(geometry_create(&geom)); geom->gmsh_dimTags_n = tagoutn; geom->gmsh_dimTags = MEM_ALLOC(allocator, tagoutn * sizeof(*tagout)); if(!geom->gmsh_dimTags) { @@ -1481,12 +1847,16 @@ exit: gmshFree(mapn); gmshFree(tagout); free_gmsh_map(map, mapnn); +#ifdef FIX_GMSH_BOOLEAN_OUTPUTS + gmshFree(tmp1); + gmshFree(tmp2); +#endif return res; error: if(ierr) { int dim = INT_MAX; - if(!mixed_dim_err_msg(name, "cut", geometries, geometries_count, &dim)) - mixed_dim_err_msg(name, "cut", tools, tools_count, &dim); + if(!mixed_dim_err_msg("cut", geometries, geometries_count, &dim)) + mixed_dim_err_msg("cut", tools, tools_count, &dim); if(geom) { SCAD(geometry_ref_put(geom)); geom = NULL; @@ -1497,9 +1867,8 @@ error: } res_T -scad_intersect_geometries - (const char* name, /* Can be NULL */ - struct scad_geometry** geometries, +scad_geometries_intersect + (struct scad_geometry** geometries, const size_t geometries_count, struct scad_geometry** tools, const size_t tools_count, @@ -1516,6 +1885,10 @@ scad_intersect_geometries struct scad_geometry* geom = NULL; struct scad_device* dev = get_device(); struct mem_allocator* allocator = NULL; +#ifdef FIX_GMSH_BOOLEAN_OUTPUTS + int *tmp1 = NULL, *tmp2 = NULL; + size_t c1, c2; +#endif if(!geometries || !geometries_count || !tools || !tools_count || !out_geometry) { res = RES_BAD_ARG; @@ -1528,17 +1901,28 @@ scad_intersect_geometries ERR(gather_tags(geometries, geometries_count, &data1, &sz1)); ERR(gather_tags(tools, tools_count, &data2, &sz2)); +#ifdef FIX_GMSH_BOOLEAN_OUTPUTS + gmshModelOccCopy(data1, sz1, &tmp1, &c1, &ierr); + ERR(gmsh_err_to_res_T(ierr)); + gmshModelOccCopy(data2, sz2, &tmp2, &c2, &ierr); + ERR(gmsh_err_to_res_T(ierr)); + /* We do remove gmsh objects here as they are temporary copies created to + * allow the remove flag to be used (gmsh is broken here if remove=0) */ + gmshModelOccIntersect(tmp1, c1, tmp2, c2, &tagout, &tagoutn, &map, + &mapn, &mapnn, -1, 1, 1, &ierr); +#else /* We don't remove gmsh objects here; they are only removed when their tags are * no longer used by any star-cad geometry */ gmshModelOccIntersect(data1, sz1, data2, sz2, &tagout, &tagoutn, &map, &mapn, &mapnn, -1, 0, 0, &ierr); +#endif + + ASSERT(tagoutn % 2 == 0); ERR(gmsh_err_to_res_T(ierr)); - ERR(geometry_create(name, &geom)); + ERR(geometry_create(&geom)); geom->gmsh_dimTags_n = tagoutn; - if (tagoutn == 0){ - geom->gmsh_dimTags = NULL; - } else { + if(tagoutn != 0){ geom->gmsh_dimTags = MEM_ALLOC(allocator, tagoutn * sizeof(*tagout)); if(!geom->gmsh_dimTags) { res = RES_MEM_ERR; @@ -1558,6 +1942,10 @@ exit: gmshFree(mapn); gmshFree(tagout); free_gmsh_map(map, mapnn); +#ifdef FIX_GMSH_BOOLEAN_OUTPUTS + gmshFree(tmp1); + gmshFree(tmp2); +#endif return res; error: if(geom) { @@ -1570,34 +1958,35 @@ error: res_T scad_geometries_common_boundaries - (const char* name, /* Can be NULL */ - struct scad_geometry** geometries, + (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_boundaries, + size_t *out_count) { res_T res = RES_OK; int* tagout = NULL; - int** map = NULL; - size_t* mapn = NULL; - size_t tagoutn, mapnn = 0, sz1, sz2; + size_t tagoutn, sz1, sz2, u_sz; int* data1 = NULL; int* data2 = NULL; + int* unique = NULL; int ierr = 0; int* bound1 = NULL; int* bound2 = NULL; size_t n1, n2; - struct scad_geometry* geom = NULL; + struct scad_geometry** out_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; + int log; + enum log_type log_type; + size_t i, c = 0, n; struct str msg; - int init = 0; + int msg_initialized = 0; - if(!geometries || !geometries_count || !tools || !tools_count || !out_geometry) { + if(!geometries || !geometries_count || !tools || !tools_count + || !out_boundaries || !out_count) + { res = RES_BAD_ARG; goto error; } @@ -1608,39 +1997,30 @@ scad_geometries_common_boundaries 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 longer used by any star-cad geometry */ + /* data1 and data2 can have tags in common: deduplicate them! + * (even if the refcounting stuff can manage duplicates) */ + ERR(process_tag_list(data1, sz1, data2, sz2, UNIQUE_TAGS, &unique, &u_sz)); + + ERR(sync_device()); gmshModelGetBoundary(data1, sz1, &bound1, &n1, 1, 0, 0, &ierr); ERR(gmsh_err_to_res_T(ierr)); gmshModelGetBoundary(data2, sz2, &bound2, &n2, 1, 0, 0, &ierr); ERR(gmsh_err_to_res_T(ierr)); - gmshModelOccIntersect(bound1, n1, bound2, n2, &tagout, &tagoutn, &map, - &mapn, &mapnn, -1, 0/*no delete*/, 0/*no delete*/, &ierr); - ERR(gmsh_err_to_res_T(ierr)); - ERR(geometry_create(name, &geom)); - geom->gmsh_dimTags_n = tagoutn; + ERR(process_tag_list(bound1, n1, bound2, n2, COMMON_TAGS, &tagout, &tagoutn)); + ASSERT(tagoutn % 2 == 0); + c = tagoutn / 2; + if(tagoutn == 0) { - if(name) { - log_message(dev, "Common boundary '%s' is empty.\n", name); - } else { - log_message(dev, "Unnamed common boundary %p is empty.\n", (void*)geom); - } - geom->gmsh_dimTags = NULL; - } else { - geom->gmsh_dimTags = MEM_ALLOC(allocator, tagoutn * sizeof(*tagout)); - if(!geom->gmsh_dimTags) { - res = RES_MEM_ERR; - goto error; - } - memcpy(geom->gmsh_dimTags, tagout, tagoutn * sizeof(*tagout)); + log_message(dev, "Common boundary list is empty.\n"); + goto exit; } - ERR(device_register_tags(geom)); - + log = (dev->options.Misc.LogRefCounting & SCAD_LOG_DIMTAGS_ALL); + log_type = dev->log_type; if(log) { str_init(allocator, &msg); - init = 1; + msg_initialized = 1; logger_print(dev->logger, log_type, "Common boundaries specific tag management:\n"); ERR(str_printf(&msg, " tags [")); @@ -1650,59 +2030,73 @@ scad_geometries_common_boundaries 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]; + for(i = 0; i < u_sz; i += 2) { + const int dim = unique[i]; + const int tag = unique[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) { + + out_geom = MEM_CALLOC(allocator, c, sizeof(*out_geom)); + if(!out_geom) { + res = RES_MEM_ERR; + goto error; + } + + for(i = 0, n = 0; i < tagoutn; i += 2, n++) { int dim = tagout[i]; int tag = tagout[i+1]; - struct tag_desc* desc = device_get_description(dim, tag); + struct tag_desc* desc; ASSERT(dim == 2); - if(!desc) { - res = RES_BAD_OP; + ERR(geometry_create(out_geom+n)); + out_geom[n]->gmsh_dimTags_n = 2; + out_geom[n]->gmsh_dimTags = MEM_ALLOC(allocator, + 2 * sizeof(*out_geom[n]->gmsh_dimTags)); + if(!out_geom[n]->gmsh_dimTags) { + res = RES_MEM_ERR; goto error; } + out_geom[n]->gmsh_dimTags[0] = dim; + out_geom[n]->gmsh_dimTags[1] = tag; + ERR(device_register_tags(out_geom[n])); /* 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)); + ERR(device_register_ref_to_tags(dim, tag, unique, u_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 = device_get_description(dim, tag); + ASSERT(desc); desc->delete_policy = Scad_do_not_delete; } exit: - if(init) str_release(&msg); - if(out_geometry) *out_geometry = geom; + if(msg_initialized) str_release(&msg); + if(out_boundaries) *out_boundaries = out_geom; + if(out_count) *out_count = c; if(allocator) { MEM_RM(allocator, data1); MEM_RM(allocator, data2); + MEM_RM(allocator, unique); + MEM_RM(allocator, tagout); } gmshFree(bound1); gmshFree(bound2); - gmshFree(mapn); - gmshFree(tagout); - free_gmsh_map(map, mapnn); return res; error: if(ierr) { int dim = INT_MAX; - if(!mixed_dim_err_msg(name, "common boundary", geometries, geometries_count, &dim)) - mixed_dim_err_msg(name, "common boundary", tools, tools_count, &dim); + if(!mixed_dim_err_msg("common boundary", geometries, geometries_count, &dim)) + mixed_dim_err_msg("common boundary", tools, tools_count, &dim); } - if(geom) { - SCAD(geometry_ref_put(geom)); - geom = NULL; + if(out_geom) { + for(i = 0; i < c; i++) { + if(out_geom[i]) SCAD(geometry_ref_put(out_geom[i])); + } + MEM_RM(allocator, out_geom); + out_geom = NULL; + c = 0; } if(tagout) gmshModelOccRemove(tagout, tagoutn, 1, &ierr); goto exit; @@ -1714,7 +2108,6 @@ scad_geometry_rotate const double pt[3], const double axis[3], const double angle, - const char* name, /* Can be NULL */ struct scad_geometry** out_geometry) { int ierr = 0; @@ -1728,11 +2121,10 @@ scad_geometry_rotate ERR(check_device(FUNC_NAME)); - ERR(scad_geometry_copy(geom, name, &out)); + ERR(scad_geometry_copy(geom, &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: @@ -1746,8 +2138,7 @@ error: res_T scad_geometry_extrude - (struct scad_geometry* geom, - const char* name, + (const struct scad_geometry* geom, const double dxdydz[3], struct scad_geometry** out_geometry) { @@ -1755,16 +2146,13 @@ scad_geometry_extrude int *tagout = NULL; size_t tagoutn; 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; + int log; + enum log_type log_type; struct mem_allocator* allocator = NULL; struct str msg; int init = 0; @@ -1777,10 +2165,27 @@ scad_geometry_extrude ERR(check_device(FUNC_NAME)); allocator = dev->allocator; + /* OCC cannot extrude 3D geometries */ + for(i = 0; i < geom->gmsh_dimTags_n; i += 2) { + const int dim = geom->gmsh_dimTags[i]; + if(dim != 2) { + if(str_is_empty(&geom->name)) { + log_error(get_device(), + "Cannot extrude unnamed 3D geometry '%p'.\n", + (void*)geom); + } else { + log_error(get_device(), + "Cannot extrude 3D geometry '%s'.\n", + str_cget(&geom->name)); + } + res = RES_BAD_ARG; + goto error; + } + } + gmshModelOccExtrude(geom->gmsh_dimTags, geom->gmsh_dimTags_n, SPLIT3(dxdydz), &tagout, &tagoutn, NULL, 0, NULL, 0, 0, &ierr); ERR(gmsh_err_to_res_T(ierr)); - get_device()->need_synchro = 1; /* Output includes both the 3D result and its 2D constituents. * Keep only 3D entities. */ @@ -1797,24 +2202,20 @@ scad_geometry_extrude 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) { *ed++ = dim; *ed++ = tag; } } - ERR(geometry_create(name, &extrude_geom)); + ERR(geometry_create(&extrude_geom)); extrude_geom->gmsh_dimTags_n = extrude_sz; extrude_geom->gmsh_dimTags = extrude_data; ERR(device_register_tags(extrude_geom)); + log = (dev->options.Misc.LogRefCounting & SCAD_LOG_DIMTAGS_ALL); + log_type = dev->log_type; if(log) { str_init(allocator, &msg); init = 1; @@ -1838,15 +2239,12 @@ scad_geometry_extrude 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; - } + ASSERT(desc != NULL); /* 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 + * deleted, they shouldn't be deleted when the geometry they belongs to is * released. */ desc->delete_policy = Scad_do_not_delete; } @@ -1868,21 +2266,19 @@ error: res_T scad_geometry_explode (const struct scad_geometry* geom, - const char* prefix_name, /* Can be NULL */ struct scad_geometry*** out_geometry, - size_t* out_geometry_n) + size_t* out_n) { res_T res = RES_OK; int* data = NULL; - size_t i, sz = 0; + size_t i, n, sz = 0; struct scad_geometry** geom_array = NULL; struct str name; int name_initialized = 0; struct scad_device* dev = get_device(); struct mem_allocator* allocator = NULL; - const char* base_name = NULL; - if(!geom || !out_geometry || !out_geometry_n) { + if(!geom || !out_geometry || !out_n) { res = RES_BAD_ARG; goto error; } @@ -1900,42 +2296,32 @@ scad_geometry_explode goto error; } - 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) { - if(base_name) { - ERR(str_set(&name, base_name)); - ERR(str_append_printf(&name,"_%lu", (unsigned long)i)); - ERR(geometry_create(str_cget(&name), geom_array+i)); - } else { - ERR(geometry_create(NULL, geom_array+i)); - } - geom_array[i]->gmsh_dimTags_n = 2; - geom_array[i]->gmsh_dimTags - = MEM_ALLOC(allocator, 2 * sizeof(*geom_array[i]->gmsh_dimTags)); - if(!geom_array[i]->gmsh_dimTags) { + for(i = 0, n = 0; i < sz; i += 2, n++) { + const int dim = data[i]; + const int tag = data[i+1]; + ERR(geometry_create(geom_array+n)); + geom_array[n]->gmsh_dimTags_n = 2; + geom_array[n]->gmsh_dimTags + = MEM_ALLOC(allocator, 2 * sizeof(*geom_array[0]->gmsh_dimTags)); + if(!geom_array[n]->gmsh_dimTags) { res = RES_MEM_ERR; goto error; } - geom_array[i]->gmsh_dimTags[0] = data[2*i]; - geom_array[i]->gmsh_dimTags[1] = data[2*i+1]; + geom_array[n]->gmsh_dimTags[0] = dim; + geom_array[n]->gmsh_dimTags[1] = tag; - ERR(device_register_tags(geom_array[i])); + ERR(device_register_tags(geom_array[n])); } exit: - if(out_geometry_n) *out_geometry_n = sz/2 ; + if(out_n) *out_n = sz/2 ; if(out_geometry) *out_geometry = geom_array; if(name_initialized) str_release(&name); return res; error: if(geom_array) { - for(i = 0; i < sz/2; i++) { - if(geom_array[i]) SCAD(geometry_ref_put(geom_array[i])); + for(i = 0, n = 0; i < sz; i += 2, n++) { + if(geom_array[n]) SCAD(geometry_ref_put(geom_array[n])); } MEM_RM(allocator, geom_array); geom_array = NULL; @@ -1947,7 +2333,6 @@ error: res_T scad_geometry_copy (const struct scad_geometry* geom, - const char* name, /* Can be NULL */ struct scad_geometry** out_geometry) { res_T res = RES_OK; @@ -1970,10 +2355,11 @@ scad_geometry_copy sz1 = geom->gmsh_dimTags_n; data1 = geom->gmsh_dimTags; gmshModelOccCopy(data1, sz1, &tagout, &tagoutn, &ierr); - get_device()->need_synchro = 1; + + ASSERT(tagoutn % 2 == 0); ERR(gmsh_err_to_res_T(ierr)); - ERR(geometry_create(name, &copy)); + ERR(geometry_create(&copy)); copy->gmsh_dimTags_n = tagoutn; copy->gmsh_dimTags = MEM_ALLOC(allocator, tagoutn * sizeof(*tagout)); if(!copy->gmsh_dimTags) { @@ -1998,11 +2384,60 @@ error: } res_T -scad_geometry_rename +scad_geometries_set_name + (struct scad_geometry** geometries, + const size_t geometries_count, + const char* prefix_name, /* Can be NULL */ + const size_t from_rank) /* Can be NULL */ +{ + res_T res = RES_OK; + struct str tmp; + int initialized = 0; + struct mem_allocator* allocator = NULL; + size_t i, cpt; + + if(!geometries || geometries_count == 0) { + res = RES_BAD_ARG; + goto error; + } + + ERR(check_device(FUNC_NAME)); + + if(!prefix_name) { + for(i = 0; i < geometries_count; i++) { + ERR(geom_set_name(geometries[i], NULL)); + } + } + else if(strlen(prefix_name) == 0) { + log_error(get_device(), "Geometry name \"\" is invalid.\n"); + res = RES_BAD_ARG; + goto error; + } else { + str_init(allocator, &tmp); + initialized = 1; + cpt = from_rank; + for(i = 0; i < geometries_count; i++) { + ERR(str_printf(&tmp, "%s_%ld", prefix_name, cpt++)); + ERR(geom_set_name(geometries[i], &tmp)); + } + } + +exit: + if(initialized) str_release(&tmp); + return res; +error: + goto exit; +} + +res_T +scad_geometry_set_name (struct scad_geometry* geom, const char* name) /* Can be NULL */ { res_T res = RES_OK; + struct str tmp; + int initialized = 0; + struct mem_allocator* allocator = NULL; if(!geom) { res = RES_BAD_ARG; @@ -2011,9 +2446,22 @@ scad_geometry_rename ERR(check_device(FUNC_NAME)); - ERR(geom_set_name(geom, name)); + if(!name) { + ERR(geom_set_name(geom, NULL)); + } + else if(strlen(name) == 0) { + log_error(get_device(), "Geometry name \"\" is invalid.\n"); + res = RES_BAD_ARG; + goto error; + } else { + str_init(allocator, &tmp); + initialized = 1; + ERR(str_set(&tmp, name)); + ERR(geom_set_name(geom, &tmp)); + } exit: + if(initialized) str_release(&tmp); return res; error: goto exit; @@ -2023,7 +2471,6 @@ res_T 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; @@ -2037,11 +2484,10 @@ scad_geometry_translate ERR(check_device(FUNC_NAME)); - ERR(scad_geometry_copy(geom, name, &out)); + ERR(scad_geometry_copy(geom, &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: @@ -2094,8 +2540,8 @@ scad_geometries_partition int dont_call_fragment = 0; char* overlap = NULL; const int invalid_flags = - ~(Scad_allow_overlapping | Scad_dump_on_overlapping_error); - const int dump_overlapping_err = flags & Scad_dump_on_overlapping_error; + ~(SCAD_ALLOW_OVERLAPPING | SCAD_DUMP_ON_OVERLAPPING_ERROR); + const int dump_overlapping_err = flags & SCAD_DUMP_ON_OVERLAPPING_ERROR; static size_t err_cpt = 0; struct str tmp; @@ -2144,10 +2590,8 @@ scad_geometries_partition ERR(gmsh_err_to_res_T(ierr)); ASSERT(sz == 2*mapnn); /* Because input tags where deduplicated */ - get_device()->need_synchro = 1; - /* Check first if there was an overlapping problem */ - if(!(flags & Scad_allow_overlapping)) { + if(!(flags & SCAD_ALLOW_OVERLAPPING)) { /* No overlapping means that each tag in geometries is translated into a * single tag in map */ size_t ov = 0; @@ -2185,25 +2629,25 @@ scad_geometries_partition str_printf(&tmp, "unamed_partition_error_%lu_%lu", err_cpt, (long unsigned)item_cpt++); tmp_err = - scad_stl_export(g, str_cget(&tmp), Scad_keep_normals_unchanged, 0); + scad_stl_export(g, str_cget(&tmp), SCAD_KEEP_NORMALS_UNCHANGED, 0); if(tmp_err == RES_OK) { log_error(get_device(), "Unnamed geometry '%p' overlapping (dumped in '%s).\n", (void*)g, str_cget(&tmp)); } else { - log_error(get_device(), "Could not dump geoemtry.\n"); + log_error(get_device(), "Could not dump geometry.\n"); } } else { str_printf(&tmp, "%s_partition_error_%lu_%lu", str_cget(&g->name), err_cpt, (long unsigned)item_cpt++); tmp_err = - scad_stl_export(g, str_cget(&tmp), Scad_keep_normals_unchanged, 0); + scad_stl_export(g, str_cget(&tmp), SCAD_KEEP_NORMALS_UNCHANGED, 0); if(tmp_err == RES_OK) { log_error(get_device(), "Geometry '%s' overlapping (dumped in '%s).\n", str_cget(&g->name), str_cget(&tmp)); } else { - log_error(get_device(), "Could not dump geoemtry.\n"); + log_error(get_device(), "Could not dump geometry.\n"); } } } @@ -2292,7 +2736,7 @@ scad_geometries_partition ASSERT(c == 2*n); /* Create geometry */ - ERR(geometry_create(NULL, geoms+i)); + ERR(geometry_create(geoms+i)); geoms[i]->gmsh_dimTags_n = c; geoms[i]->gmsh_dimTags = dt; ERR(device_register_tags(geoms[i])); @@ -2326,6 +2770,9 @@ error: } if(tagout) { gmshModelOccRemove(tagout, tagoutn, 1, &ierr); + /* Do not check ierr, first because we are in error handler already, but + * also because an error is expected if these OCC entities have allready + * been freed through the call to ref_put above */ } goto exit; } @@ -2343,14 +2790,15 @@ scad_geometries_swap struct mem_allocator* allocator = NULL; struct str tmp, msg; - if(!pool1 || !pool2) { + if(!pool1 || !pool2 || !flags || count == 0) { res = RES_BAD_ARG; goto error; } + ERR(check_device(FUNC_NAME)); allocator = dev->allocator; - if(flags & Scad_swap_name) str_init(allocator, &tmp); - if(flags & Scad_swap_geometry && dev->log) str_init(allocator, &msg); + 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; @@ -2358,7 +2806,7 @@ scad_geometries_swap if(pool1[i] == pool2[i]) continue; /* Swap content according to flags. Don't swap refcount! */ - if(flags & Scad_swap_name) { + if(flags & SCAD_SWAP_NAME) { if(dev->log) { if(str_is_empty(&g1->name) && str_is_empty(&g2->name)) { /* Do nothing */ @@ -2396,7 +2844,7 @@ scad_geometries_swap ERR(str_copy(&g2->name, &tmp)); } } - if(flags & Scad_swap_geometry) { + if(flags & SCAD_SWAP_GEOMETRY) { /* Swap in tag2geom tables */ size_t n; if(dev->log) { @@ -2431,8 +2879,7 @@ scad_geometries_swap 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)); + ERR(str_append_printf(&msg, (n ? ", %d.%d" : "%d.%d"), dim, tag)); } logger_print(dev->logger, dev->log_type, "%s.\n", str_cget(&msg)); if(str_is_empty(&g2->name)) { @@ -2457,35 +2904,35 @@ scad_geometries_swap } exit: - if(flags & Scad_swap_name) str_release(&tmp); - if(flags & Scad_swap_geometry && dev->log) str_release(&msg); + if(flags & SCAD_SWAP_NAME) str_release(&tmp); + if(flags & SCAD_SWAP_GEOMETRY && dev->log) str_release(&msg); return res; error: goto exit; } res_T -scad_geometry_boundary - (const char* name, - struct scad_geometry** geometries, +scad_geometries_boundaries + (struct scad_geometry** geometries, const size_t geometries_count, - struct scad_geometry** out_geometry) + struct scad_geometry*** out_boundaries, + size_t *out_count) { res_T res = RES_OK; int* tagout = NULL; size_t tagoutn, sz; int* data = NULL; int ierr = 0; - struct scad_geometry* geom = NULL; + struct scad_geometry** out_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; + int log; + enum log_type log_type; + size_t i, c = 0, n; struct str msg; - int init = 0; + int msg_initialized = 0; - if(!geometries || !out_geometry) { + if(!geometries || geometries_count == 0 || !out_boundaries || !out_count) { res = RES_BAD_ARG; goto error; } @@ -2494,23 +2941,23 @@ scad_geometry_boundary allocator = dev->allocator; ERR(gather_tags(geometries, geometries_count, &data, &sz)); + ERR(sync_device()); gmshModelGetBoundary(data, sz, &tagout, &tagoutn, 1, 0, 0, &ierr); ERR(gmsh_err_to_res_T(ierr)); - ERR(geometry_create(name, &geom)); - geom->gmsh_dimTags_n = tagoutn; - geom->gmsh_dimTags = MEM_ALLOC(allocator, tagoutn * 2 * sizeof(*tagout)); - if(!geom->gmsh_dimTags) { + ASSERT(tagoutn % 2 == 0); + c = tagoutn / 2; + out_geom = MEM_CALLOC(allocator, c, sizeof(*out_geom)); + if(!out_geom) { res = RES_MEM_ERR; goto error; } - memcpy(geom->gmsh_dimTags, tagout, tagoutn * sizeof(*tagout)); - - ERR(device_register_tags(geom)); + log = (dev->options.Misc.LogRefCounting & SCAD_LOG_DIMTAGS_ALL); + log_type = dev->log_type; if(log) { str_init(allocator, &msg); - init = 1; + msg_initialized = 1; logger_print(dev->logger, log_type, "Boundary specific tag management:\n"); ERR(str_printf(&msg, " tags [")); for(i = 0; i < tagoutn; i += 2) { @@ -2526,34 +2973,50 @@ scad_geometry_boundary } logger_print(dev->logger, log_type, "%s].\n", str_cget(&msg)); } - for(i = 0; i < tagoutn; i += 2) { + + for(i = 0, n = 0; i < tagoutn; i += 2, n++) { int dim = tagout[i]; int tag = tagout[i+1]; - struct tag_desc* desc = device_get_description(dim, tag); + struct tag_desc* desc = NULL; ASSERT(dim == 2); - if(!desc) { - res = RES_BAD_OP; + ERR(geometry_create(out_geom+n)); + out_geom[n]->gmsh_dimTags_n = 2; + out_geom[n]->gmsh_dimTags = MEM_ALLOC(allocator, 2 * sizeof(*tagout)); + if(!out_geom[n]->gmsh_dimTags) { + res = RES_MEM_ERR; goto error; } + out_geom[n]->gmsh_dimTags[0] = dim; + out_geom[n]->gmsh_dimTags[1] = tag; + + ERR(device_register_tags(out_geom[n])); + /* 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 = device_get_description(dim, tag); + ASSERT(desc); desc->delete_policy = Scad_do_not_delete; } exit: - if(init) str_release(&msg); + if(msg_initialized) str_release(&msg); if(allocator) MEM_RM(allocator, data); - if(out_geometry) *out_geometry = geom; + if(out_boundaries) *out_boundaries = out_geom; + if(out_count) *out_count = c; gmshFree(tagout); return res; error: - if(geom) { - SCAD(geometry_ref_put(geom)); - geom = NULL; + if(out_geom) { + for(i = 0; i < c; i++) { + if(out_geom[i]) SCAD(geometry_ref_put(out_geom[i])); + } + MEM_RM(allocator, out_geom); + out_geom = NULL; + c = 0; } if(tagout) gmshModelOccRemove(tagout, tagoutn, 1, &ierr); goto exit; @@ -2562,9 +3025,8 @@ error: res_T scad_step_import (const char* filename, - const char* name, struct scad_geometry*** out_geometry, - size_t* out_geometry_n) + size_t* out_n) { int ierr; int* tagout = NULL; @@ -2576,7 +3038,7 @@ scad_step_import struct scad_device* dev = get_device(); res_T res = RES_OK; - if(!filename || !out_geometry || !out_geometry_n) { + if(!filename || !out_geometry || !out_n) { res = RES_BAD_ARG; goto error; } @@ -2599,14 +3061,7 @@ scad_step_import str_init(allocator, &strname); name_initialized = 1; for(i=0; i<ga_sz; ++i) { - if (name) { - ERR(str_set(&strname, name)); - ERR(str_append_printf(&strname,"_%lu", (unsigned long)i)); - ERR(geometry_create(str_cget(&strname), geom_array+i)); - } else { - ERR(geometry_create(NULL, geom_array+i)); - } - + ERR(geometry_create(geom_array+i)); geom_array[i]->gmsh_dimTags_n = 2; geom_array[i]->gmsh_dimTags = MEM_ALLOC(allocator, 2 * sizeof(*geom_array[i]->gmsh_dimTags)); @@ -2622,7 +3077,7 @@ scad_step_import exit: gmshFree(tagout); - if(out_geometry_n) *out_geometry_n = ga_sz ; + if(out_n) *out_n = ga_sz ; if(out_geometry) *out_geometry = geom_array; if(name_initialized) str_release(&strname); return res; @@ -2640,11 +3095,10 @@ error: } res_T -scad_geometry_normal +scad_geometry_get_normal (struct scad_geometry* geom, - double p[3], + const double p[3], double N[3], - const char* name, /* Can be NULL */ struct scad_geometry** out_geometry) { res_T res = RES_OK; @@ -2654,16 +3108,19 @@ scad_geometry_normal size_t sz = 0; 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; + int log; + enum log_type log_type; struct mem_allocator* allocator = NULL; double* coord = NULL; double* pcoord = NULL; double* normals = NULL; struct darray_int tags; int initialized = 0; + double d, min_d = DBL_MAX, pcoord_min[2]; + int min_tag = -1; + size_t normals_n; - if(!geom || !p || !N || !out_geometry) { + if(!geom || !p || !N) { res = RES_BAD_ARG; goto error; } @@ -2678,71 +3135,67 @@ scad_geometry_normal data = darray_int_cdata_get(&tags); sz = darray_int_size_get(&tags); + log = (dev->options.Misc.LogRefCounting & SCAD_LOG_DIMTAGS_ALL); + log_type = dev->log_type; + + /* Find the closest point on tags of geom */ for(i = 0; i < sz; ++i) { size_t pcoord_n; size_t coord_n; - size_t normals_n; const int dim = 2; int tag = data[i]; + double tmp[3]; 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(geometry_create(name, &out)); - out->gmsh_dimTags = MEM_ALLOC(allocator, 2 * sizeof(*out->gmsh_dimTags)); - if(!out->gmsh_dimTags) { - res = RES_MEM_ERR; - goto error; - } - out->gmsh_dimTags_n = 2; - out->gmsh_dimTags[0] = dim; - out->gmsh_dimTags[1] = tag; - d3_set(N, normals); - - ERR(device_register_tags(out)); - - /* 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; + d = d3_len(d3_sub(tmp, p, coord)); + if(d < min_d) { + min_d = d; + min_tag = tag; + d2_set(pcoord_min, pcoord); } - gmshFree(coord); gmshFree(pcoord); coord = pcoord = NULL; } - if(!out) { /* Could not find a matching surface */ - if(str_is_empty(&geom->name)) { - log_warning(get_device(), - "Could not get normal at vertex %g %g %g " - "as unamed geometry %p is not close enough.\n", - SPLIT3(p), (void*)geom); - } else { - log_warning(get_device(), - "Could not get normal at vertex %g %g %g " - "as geometry %s is not close enough.\n", - SPLIT3(p), str_cget(&geom->name)); + if(min_d == DBL_MAX) { /* At least if sz==0 */ + goto exit; + } + + /* Get the normal at the selected point */ + gmshModelGetNormal(min_tag, pcoord_min, 2, &normals, &normals_n, &ierr); + ERR(gmsh_err_to_res_T(ierr)); + ASSERT(normals_n == 3); + d3_set(N, normals); + + /* Create a geometry if required */ + if(out_geometry) { + ERR(geometry_create(&out)); + out->gmsh_dimTags = MEM_ALLOC(allocator, 2 * sizeof(*out->gmsh_dimTags)); + if(!out->gmsh_dimTags) { + res = RES_MEM_ERR; + goto error; + } + out->gmsh_dimTags_n = 2; + out->gmsh_dimTags[0] = 2; + out->gmsh_dimTags[1] = min_tag; + ERR(device_register_tags(out)); + + /* 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", 2, min_tag); + } + ERR(device_register_ref_to_tags(2, min_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", 2, min_tag); } - res = RES_BAD_ARG; - goto error; } exit: @@ -2753,6 +3206,10 @@ exit: if(out_geometry) *out_geometry = out; return res; error: + if(out) { + SCAD(geometry_ref_put(out)); + out = NULL; + } goto exit; } @@ -2761,7 +3218,6 @@ scad_geometry_dilate (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; @@ -2775,7 +3231,7 @@ scad_geometry_dilate ERR(check_device(FUNC_NAME)); - ERR(scad_geometry_copy(geom, name, &out)); + ERR(scad_geometry_copy(geom, &out)); gmshModelOccDilate(out->gmsh_dimTags, out->gmsh_dimTags_n, SPLIT3(center), SPLIT3(scale), &ierr); @@ -2803,14 +3259,16 @@ scad_geometries_set_mesh_size_modifier int* tagout[4] = { NULL, NULL, NULL, NULL }; size_t tagoutn[4] = { 0, 0, 0, 0}, i; - if(!geometries || geometries_count == 0 || modifier <= 0) { + if(!geometries || geometries_count == 0 || modifier <= 0 + || (type != SCAD_ABSOLUTE_SIZE && type != SCAD_SIZE_FACTOR)) + { res = RES_BAD_ARG; goto error; } ERR(check_device(FUNC_NAME)); - if(type == Scad_absolute_size) modifier = -modifier; + if(type == SCAD_ABSOLUTE_SIZE) modifier = -modifier; ERR(gather_tags_recursive(geometries, geometries_count, 0, tagout, tagoutn)); for(dim = 0; dim < 4; dim++) { for(i = 0; i < tagoutn[dim]; i += 2) { @@ -2836,12 +3294,14 @@ scad_geometries_set_mesh_size_modifier } } - gmshModelMeshSetSizeCallback( (some ? size_callback : NULL), NULL, &ierr); + gmshModelMeshSetSizeCallback((some ? size_callback : NULL), NULL, &ierr); ERR(gmsh_err_to_res_T(ierr)); exit: - for(dim = 0; dim < 4; dim++) { - MEM_RM(dev->allocator, tagout[dim]); + if(dev) { + for(dim = 0; dim < 4; dim++) { + MEM_RM(dev->allocator, tagout[dim]); + } } return res; error: @@ -2860,7 +3320,14 @@ scad_geometries_set_mesh_algorithm int* tagout[4] = { NULL, NULL, NULL, NULL }; size_t tagoutn[4], i; - if(!geometries || geometries_count == 0) { + if(!geometries || geometries_count == 0 || + (algorithm != SCAD_MESHADAPT && + algorithm != SCAD_AUTOMATIC && + algorithm != SCAD_INITIAL_MESH_ONLY && + algorithm != SCAD_DELAUNAY && + algorithm != SCAD_FRONTAL_DELAUNAY && + algorithm != SCAD_QUASI_STRUCTURED)) + { res = RES_BAD_ARG; goto error; } @@ -2876,8 +3343,10 @@ scad_geometries_set_mesh_algorithm } exit: - for(i = 2; i < 4; i++) { - MEM_RM(dev->allocator, tagout[i]); + if(dev) { + for(i = 2; i < 4; i++) { + MEM_RM(dev->allocator, tagout[i]); + } } return res; error: @@ -2894,12 +3363,13 @@ scad_geometries_set_periodic { res_T res = RES_OK; struct scad_device* dev = get_device(); - struct mem_allocator* allocator = dev->allocator; + struct mem_allocator* allocator = NULL; int ierr; int* src_dimTagout[4] = { NULL, NULL, NULL, NULL }; int* tgt_dimTagout[4] = { NULL, NULL, NULL, NULL }; int* src_tags = NULL, *tgt_tags = NULL; - size_t src_dimTagoutn[4], tgt_dimTagoutn[4], src_tagsn = 0, tgt_tagsn = 0, i; + size_t src_dimTagoutn[4] = { 0,0,0,0 }, tgt_dimTagoutn[4] = { 0,0,0,0 }; + size_t src_tagsn = 0, tgt_tagsn = 0, i; if(!source || source_count == 0 || !target || target_count == 0 || !affine) { res = RES_BAD_ARG; @@ -2918,6 +3388,7 @@ scad_geometries_set_periodic goto error; } + allocator = dev->allocator; src_tags = MEM_ALLOC(allocator, src_tagsn); tgt_tags = MEM_ALLOC(allocator, tgt_tagsn); if(!src_tags || !tgt_tags) { @@ -2935,12 +3406,14 @@ scad_geometries_set_periodic ERR(gmsh_err_to_res_T(ierr)); exit: - for(i = 2; i < 4; i++) { - MEM_RM(dev->allocator, src_dimTagout[i]); - MEM_RM(dev->allocator, tgt_dimTagout[i]); + if(dev) { + for(i = 2; i < 4; i++) { + MEM_RM(dev->allocator, src_dimTagout[i]); + MEM_RM(dev->allocator, tgt_dimTagout[i]); + } + MEM_RM(dev->allocator, src_tags); + MEM_RM(dev->allocator, tgt_tags); } - MEM_RM(dev->allocator, src_tags); - MEM_RM(dev->allocator, tgt_tags); return res; error: goto exit; diff --git a/src/test_api.c b/src/test_api.c @@ -20,12 +20,25 @@ #include <rsys/rsys.h> #include <rsys/str.h> #include <rsys/math.h> +#include <rsys/double3.h> #include <rsys/mem_allocator.h> #include <rsys/dynamic_array_double.h> #include <stdlib.h> #include <stdio.h> +#define STRINGIZE(x) STRINGIZE2(x) +#define STRINGIZE2(x) #x +#define LINE_STRING STRINGIZE(__LINE__) + +static void +dummy_release + (void* data) +{ + (void) data; + return; +} + static void get_position (const size_t ivert, double pos[2], void* data) @@ -34,17 +47,21 @@ get_position ASSERT(pos && coord); pos[0] = coord[2*ivert]; pos[1] = coord[1+2*ivert]; - } int main(int argc, char* argv[]) { res_T res = RES_OK; - double p1[3] = {0, 0, 0}; - double p2[3] = {0.25, 0.25, 0.8}; - double d1[3] = {1, 1, 1}; - double coord[] = {0, 1.6, 0.5, 0.9, 0.8, 0.6}; + const double p1[3] = {0, 0, 0}; + const double p2[3] = {0.25, 0.25, 0.8}; + const double p3[3] = {0.85, 0, 0}; + const double p4[3] = {1, 0, 0}; + const double d0[3] = {0.1, 0.1, 0.1}; + const double d1[3] = {1, 1, 1}; + const double d2[3] = {1, 1, 0}; + const double s[3] = {1, 1, 1}; + double coord[] = {0, 1.6, 0.5, 0.9, 0.8, 0.6}; struct scad_geometry* geom1 = NULL; struct scad_geometry* geom2 = NULL; struct scad_geometry* geom = NULL; @@ -55,242 +72,1547 @@ main(int argc, char* argv[]) struct mem_allocator allocator; struct darray_double trg; struct scad_options options = SCAD_DEFAULT_OPTIONS; - const char* name; + struct scad_options opt = SCAD_DEFAULT_OPTIONS; + const char *name, *name2; + double m, tmp[3], tmp2[3]; + double affine[16] = { 1, 0, 0, 0.25, 0, 1, 0, 0.25, 0, 0, 1, 0.8, 0, 0, 0, 1 }; size_t i, c; + int e; + void* data; (void)argc; (void)argv; OK(mem_init_proxy_allocator(&allocator, &mem_default_allocator)); + options.Misc.DebugEmptyContext = 1; + options.Mesh.MeshSizeExtendFromBoundary = 0; + options.Mesh.MeshSizeFromPoints = 0; + /* cannot call any API function before a successful call to scad_initialize */ + geom1 = geom2 = geom = + (struct scad_geometry*)1; /* To allow tests go past arg!=NULL */ BAD(scad_finalize()); - BAD(scad_set_options(NULL)); - BAD(scad_synchronize()); + BAD(scad_set_options(&options)); + BAD(scad_get_options(&options)); + BAD(scad_scene_count(&c)); BAD(scad_scene_clear()); - BAD(scad_add_rectangle(NULL, p1, d1, &geom)); - BAD(scad_add_disk(NULL, p1, 1, &geom)); - BAD(scad_add_polygon(NULL, get_position, coord, 0, 3, &geom)); - BAD(scad_add_box(NULL, p1, d1, &geom)); - BAD(scad_add_cylinder(NULL, p1, d1, 2, 1, &geom)); - BAD(scad_add_sphere(NULL, p1, 1, &geom)); - BAD(scad_step_import("test.step", NULL, &geom_array, &c)); + BAD(scad_scene_write("test.step")); BAD(scad_scene_mesh()); + BAD(scad_add_rectangle(p1, d2, &geom)); + BAD(scad_add_disk(p1, 1, &geom)); + BAD(scad_add_polygon(get_position, coord, 0, 3, &geom)); + BAD(scad_add_box(p1, d1, &geom)); + BAD(scad_add_cylinder(p1, d1, 2, 1, &geom)); + BAD(scad_add_sphere(p1, 1, &geom)); + BAD(scad_add_cone(p1, d1, 1, 1, 2*PI, &geom)); + BAD(scad_add_torus(p1, 1, 1, 2*PI, NULL, &geom)); + BAD(scad_geometry_dilate(geom1, p1, p1, &geom)); + BAD(scad_geometry_translate(geom1, p1, &geom)); + BAD(scad_geometry_rotate(geom1, p1, p1, 2, &geom)); + BAD(scad_geometry_extrude(geom1, p1, &geom)); + BAD(scad_geometry_explode(geom1, &geom_array, &c)); + BAD(scad_geometry_ref_get(geom1)); + BAD(scad_geometry_ref_put(geom1)); + BAD(scad_geometry_get_count(geom1, NULL)); + BAD(scad_geometry_is_empty(geom1, &e)); + BAD(scad_geometry_set_custom_data(geom1, NULL, NULL)); + BAD(scad_geometry_get_custom_data(geom1, (void*)&trg)); + BAD(scad_geometry_set_name(geom1, name)); + BAD(scad_geometries_set_name(&geom1, 1, name, 0)); + BAD(scad_geometry_get_name(geom1, &name)); + BAD(scad_geometries_swap(&geom1, &geom2, 1, 0)); + BAD(scad_geometry_get_mass(geom1, &m)); + BAD(scad_geometry_get_centerofmass(geom1, tmp)); + BAD(scad_geometry_get_closest_point(geom1, p1, tmp, &m, NULL)); + BAD(scad_geometry_get_normal(geom1, tmp, tmp, &geom)); + BAD(scad_geometry_get_bounding_box(geom1, tmp, tmp)); + BAD(scad_geometry_equal(geom1, geom2, &e)); + BAD(scad_geometry_is_included(geom1, &geom2, 1, &e)); + BAD(scad_geometries_collect(&geom1, 1, &geom2)); + BAD(scad_geometries_fuse(&geom1, 1, &geom2, 1, &geom)); + BAD(scad_geometries_cut(&geom1, 1, &geom2, 1, &geom)); + BAD(scad_geometries_intersect(&geom1, 1, &geom2, 1, &geom)); + BAD(scad_geometries_partition(&geom1, 1, 0, &geom)); + BAD(scad_geometries_common_boundaries(&geom1, 1, &geom2, 1, &geom_array, &c)); + BAD(scad_geometries_boundaries(&geom1, 1, &geom_array, &c)); + BAD(scad_geometry_copy(geom1, &geom2)); + BAD(scad_geometry_set_visibility(geom1, 0, 0)); + BAD(scad_geometries_set_periodic(&geom1, 1, &geom2, 1, affine)); + BAD(scad_geometries_set_mesh_size_modifier(&geom1, 1, SCAD_ABSOLUTE_SIZE, 1)); + BAD(scad_geometries_set_mesh_algorithm(&geom1, 1, SCAD_QUASI_STRUCTURED)); + BAD(scad_geometries_clear_mesh(&geom1, 1)); + BAD(scad_step_import("test.step", &geom_array, &c)); + BAD(scad_stl_export(geom1, "test.stl", SCAD_KEEP_NORMALS_UNCHANGED, 0)); + BAD(scad_stl_export_partial(geom1, &geom1, 1, "test.stl", + SCAD_KEEP_NORMALS_UNCHANGED, 0)); + BAD(scad_stl_export_split(geom1, "test.stl", SCAD_KEEP_NORMALS_UNCHANGED, 0)); + BAD(scad_stl_get_data(geom1, &trg)); + BAD(scad_stl_get_data_partial(geom1, &geom2, 1, &trg)); + BAD(scad_stl_data_write(&trg, "test.stl", SCAD_KEEP_NORMALS_UNCHANGED, 0)); BAD(scad_run_ui()); - BAD(scad_geometry_get_name(geom, &name)); + BAD(scad_synchronize()); + CHK(SIZE_MAX == scad_get_dimtag_refcount(3, 0)); + BAD(scad_dump_geometry(geom)); + BAD(scad_dump_geometries()); /* cannot call any API function after a successful call to scad_finalize */ OK(scad_initialize(NULL, &allocator, 3)); OK(scad_finalize()); BAD(scad_finalize()); - BAD(scad_set_options(NULL)); - BAD(scad_synchronize()); + BAD(scad_set_options(&options)); + BAD(scad_get_options(&options)); + BAD(scad_scene_count(&c)); BAD(scad_scene_clear()); - BAD(scad_add_rectangle(NULL, p1, d1, &geom)); - BAD(scad_add_disk(NULL, p1, 1, &geom)); - BAD(scad_add_polygon(NULL, get_position, coord, 0, 3, &geom)); - BAD(scad_add_box(NULL, p1, d1, &geom)); - BAD(scad_add_cylinder(NULL, p1, d1, 2, 1, &geom)); - BAD(scad_add_sphere(NULL, p1, 1, &geom)); - BAD(scad_step_import("test.step", NULL, &geom_array, &c)); + BAD(scad_scene_write("test.step")); BAD(scad_scene_mesh()); + BAD(scad_add_rectangle(p1, d2, &geom)); + BAD(scad_add_disk(p1, 1, &geom)); + BAD(scad_add_polygon(get_position, coord, 0, 3, &geom)); + BAD(scad_add_box(p1, d1, &geom)); + BAD(scad_add_cylinder(p1, d1, 2, 1, &geom)); + BAD(scad_add_sphere(p1, 1, &geom)); + BAD(scad_add_cone(p1, d1, 1, 1, 2*PI, &geom)); + BAD(scad_add_torus(p1, 1, 1, 2*PI, NULL, &geom)); + BAD(scad_geometry_dilate(geom1, p1, p1, &geom)); + BAD(scad_geometry_translate(geom1, p1, &geom)); + BAD(scad_geometry_rotate(geom1, p1, p1, 2, &geom)); + BAD(scad_geometry_extrude(geom1, p1, &geom)); + BAD(scad_geometry_explode(geom1, &geom_array, &c)); + BAD(scad_geometry_ref_get(geom1)); + BAD(scad_geometry_ref_put(geom1)); + BAD(scad_geometry_get_count(geom1, NULL)); + BAD(scad_geometry_is_empty(geom1, &e)); + BAD(scad_geometry_set_custom_data(geom1, NULL, NULL)); + BAD(scad_geometry_get_custom_data(geom1, (void*)&trg)); + BAD(scad_geometry_set_name(geom1, name)); + BAD(scad_geometries_set_name(&geom1, 1, name, 0)); + BAD(scad_geometry_get_name(geom1, &name)); + BAD(scad_geometries_swap(&geom1, &geom2, 1, 0)); + BAD(scad_geometry_get_mass(geom1, &m)); + BAD(scad_geometry_get_centerofmass(geom1, tmp)); + BAD(scad_geometry_get_closest_point(geom1, tmp, tmp, &m, NULL)); + BAD(scad_geometry_get_normal(geom1, tmp, tmp, &geom)); + BAD(scad_geometry_get_bounding_box(geom1, tmp, tmp)); + BAD(scad_geometry_equal(geom1, geom2, &e)); + BAD(scad_geometry_is_included(geom1, &geom2, 1, &e)); + BAD(scad_geometries_collect(&geom1, 1, &geom2)); + BAD(scad_geometries_fuse(&geom1, 1, &geom2, 1, &geom)); + BAD(scad_geometries_cut(&geom1, 1, &geom2, 1, &geom)); + BAD(scad_geometries_intersect(&geom1, 1, &geom2, 1, &geom)); + BAD(scad_geometries_partition(&geom1, 1, 0, &geom)); + BAD(scad_geometries_common_boundaries(&geom1, 1, &geom2, 1, &geom_array, &c)); + BAD(scad_geometries_boundaries(&geom1, 1, &geom_array, &c)); + BAD(scad_geometry_copy(geom1, &geom2)); + BAD(scad_geometry_set_visibility(geom1, 0, 0)); + BAD(scad_geometries_set_periodic(&geom1, 1, &geom2, 1, affine)); + BAD(scad_geometries_set_mesh_size_modifier(&geom1, 1, SCAD_ABSOLUTE_SIZE, 1)); + BAD(scad_geometries_set_mesh_algorithm(&geom1, 1, SCAD_QUASI_STRUCTURED)); + BAD(scad_geometries_clear_mesh(&geom1, 1)); + BAD(scad_step_import("test.step", &geom_array, &c)); + BAD(scad_stl_export(geom1, "test.stl", SCAD_KEEP_NORMALS_UNCHANGED, 0)); + BAD(scad_stl_export_partial(geom1, &geom1, 1, "test.stl", + SCAD_KEEP_NORMALS_UNCHANGED, 0)); + BAD(scad_stl_export_split(geom1, "test.stl", SCAD_KEEP_NORMALS_UNCHANGED, 0)); + BAD(scad_stl_get_data(geom1, &trg)); + BAD(scad_stl_get_data_partial(geom1, &geom2, 1, &trg)); + BAD(scad_stl_data_write(&trg, "test.stl", SCAD_KEEP_NORMALS_UNCHANGED, 0)); BAD(scad_run_ui()); - BAD(scad_geometry_get_name(geom, &name)); + BAD(scad_synchronize()); + CHK(SIZE_MAX == scad_get_dimtag_refcount(3, 0)); + BAD(scad_dump_geometry(geom)); + BAD(scad_dump_geometries()); BAD(scad_initialize(NULL, &allocator, 4)); BAD(scad_initialize(NULL, &allocator, -1)); - OK(scad_initialize(NULL, &allocator, 3)); + OK(scad_scene_count(&c)); + CHK(c == 0); + 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; - geoms[1] = geom2; + OK(scad_scene_count(&c)); + CHK(c == 0); + + BAD(scad_get_options(NULL)); + OK(scad_get_options(&opt)); + + OK(scad_scene_count(&c)); + CHK(c == 0); + + BAD(scad_scene_count(NULL)); + OK(scad_scene_count(&c)); + CHK(c == 0); + OK(scad_add_rectangle(p1, d2, &geom)); + OK(scad_scene_count(&c)); + CHK(c == 1); + OK(scad_geometry_ref_put(geom)); + + OK(scad_scene_count(&c)); + CHK(c == 0); + + OK(scad_scene_clear()); + OK(scad_scene_count(&c)); + CHK(c == 0); + OK(scad_add_rectangle(p1, d2, &geom)); + OK(scad_scene_clear()); + OK(scad_scene_count(&c)); + CHK(c == 0); + OK(scad_add_rectangle(p1, d2, &geom)); + OK(scad_geometry_ref_get(geom)); + OK(scad_scene_clear()); + OK(scad_scene_count(&c)); + CHK(c == 0); + + OK(scad_scene_count(&c)); + CHK(c == 0); + + BAD(scad_scene_write(NULL)); + ERR(scad_scene_write("")); + ERR(scad_scene_write("/tmp/test.bad")); + OK(scad_scene_write("/tmp/test"LINE_STRING".step")); + + OK(scad_scene_count(&c)); + CHK(c == 0); + + BAD(scad_add_rectangle(NULL, NULL, NULL)); + BAD(scad_add_rectangle(NULL, NULL, &geom)); + BAD(scad_add_rectangle(NULL, d1, NULL)); + BAD(scad_add_rectangle(p1, NULL, NULL)); + BAD(scad_add_rectangle(NULL, d2, &geom)); + BAD(scad_add_rectangle(p1, NULL, &geom)); + BAD(scad_add_rectangle(p1, d2, NULL)); + OK(scad_add_rectangle(p1, d2, &geom)); + OK(scad_geometry_ref_put(geom)); + + OK(scad_scene_count(&c)); + CHK(c == 0); + + BAD(scad_add_disk(NULL, -1, NULL)); + BAD(scad_add_disk(NULL, -1, &geom)); + BAD(scad_add_disk(NULL, 1, NULL)); + BAD(scad_add_disk(p1, -1, NULL)); + BAD(scad_add_disk(NULL, 1, &geom)); + BAD(scad_add_disk(p1, -1, &geom)); + BAD(scad_add_disk(p1, 1, NULL)); + OK(scad_add_disk(p1, 1, &geom)); + OK(scad_geometry_ref_put(geom)); + + OK(scad_scene_count(&c)); + CHK(c == 0); + + BAD(scad_add_polygon(NULL, coord, 0, 0, NULL)); + BAD(scad_add_polygon(get_position, coord, 0, 0, NULL)); + BAD(scad_add_polygon(NULL, coord, 0, 3, NULL)); + BAD(scad_add_polygon(NULL, coord, 0, 0, &poly)); + BAD(scad_add_polygon(NULL, coord, 0, 3, &poly)); + BAD(scad_add_polygon(get_position, coord, 0, 0, &poly)); + BAD(scad_add_polygon(get_position, coord, 0, 3, NULL)); + OK(scad_add_polygon(get_position, coord, 0, 3, &poly)); + OK(scad_geometry_ref_put(poly)); + + OK(scad_scene_count(&c)); + CHK(c == 0); + + BAD(scad_add_box(NULL, NULL, NULL)); + BAD(scad_add_box(NULL, NULL, &geom)); + BAD(scad_add_box(NULL, d1, NULL)); + BAD(scad_add_box(p1, NULL, NULL)); + BAD(scad_add_box(NULL, d1, &geom)); + BAD(scad_add_box(p1, NULL, &geom)); + BAD(scad_add_box(p1, d1, NULL)); + OK(scad_add_box(p1, d1, &geom)); + OK(scad_geometry_ref_put(geom)); + + OK(scad_scene_count(&c)); + CHK(c == 0); + + BAD(scad_add_cylinder(NULL, NULL, -1, -1, NULL)); + BAD(scad_add_cylinder(NULL, NULL, -1, -1, &geom)); + BAD(scad_add_cylinder(NULL, NULL, -1, 1, NULL)); + BAD(scad_add_cylinder(NULL, NULL, 1, -1, NULL)); + BAD(scad_add_cylinder(NULL, d1, -1, -1, NULL)); + BAD(scad_add_cylinder(p1, NULL, -1, -1, NULL)); + BAD(scad_add_cylinder(NULL, NULL, -1, 1, &geom)); + BAD(scad_add_cylinder(NULL, NULL, 1, -1, &geom)); + BAD(scad_add_cylinder(NULL, d1, -1, -1, &geom)); + BAD(scad_add_cylinder(p1, NULL, -1, -1, &geom)); + BAD(scad_add_cylinder(NULL, NULL, 1, 1, NULL)); + BAD(scad_add_cylinder(NULL, d1, -1, 1, NULL)); + BAD(scad_add_cylinder(p1, NULL, -1, 1, NULL)); + BAD(scad_add_cylinder(NULL, d1, 1, -1, NULL)); + BAD(scad_add_cylinder(p1, NULL, 1, -1, NULL)); + BAD(scad_add_cylinder(p1, d1, -1, -1, NULL)); + BAD(scad_add_cylinder(NULL, NULL, 1, 1, &geom)); + BAD(scad_add_cylinder(NULL, d1, -1, 1, &geom)); + BAD(scad_add_cylinder(p1, NULL, -1, 1, &geom)); + BAD(scad_add_cylinder(NULL, d1, 1, -1, &geom)); + BAD(scad_add_cylinder(p1, NULL, 1, -1, &geom)); + BAD(scad_add_cylinder(p1, d1, -1, -1, &geom)); + BAD(scad_add_cylinder(NULL, d1, 1, 1, NULL)); + BAD(scad_add_cylinder(p1, NULL, 1, 1, NULL)); + BAD(scad_add_cylinder(p1, d1, -1, 1, NULL)); + BAD(scad_add_cylinder(p1, d1, 1, -1, NULL)); + BAD(scad_add_cylinder(NULL, d1, 1, 1, &geom)); + BAD(scad_add_cylinder(p1, NULL, 1, 1, &geom)); + BAD(scad_add_cylinder(p1, d1, -1, 1, &geom)); + BAD(scad_add_cylinder(p1, d1, 1, 3*PI, &geom)); + BAD(scad_add_cylinder(p1, d1, 1, 1, NULL)); + OK(scad_add_cylinder(p1, d1, 1, 1, &geom)); + OK(scad_geometry_ref_put(geom)); + + OK(scad_scene_count(&c)); + CHK(c == 0); + + BAD(scad_add_sphere(NULL, -1, NULL)); + BAD(scad_add_sphere(NULL, -1, &geom)); + BAD(scad_add_sphere(NULL, 1, NULL)); + BAD(scad_add_sphere(p1, -1, NULL)); + BAD(scad_add_sphere(NULL, 1, &geom)); + BAD(scad_add_sphere(p1, 0, &geom)); + BAD(scad_add_sphere(p1, 1, NULL)); + OK(scad_add_sphere(p1, 1, &geom)); + OK(scad_geometry_ref_put(geom)); + + OK(scad_scene_count(&c)); + CHK(c == 0); + + BAD(scad_add_cone(NULL, NULL, -1, -1, -1, NULL)); + BAD(scad_add_cone(NULL, NULL, -1, -1, -1, &geom)); + BAD(scad_add_cone(NULL, NULL, -1, -1, 2*PI, NULL)); + BAD(scad_add_cone(NULL, NULL, -1, -1, 2*PI, &geom)); + BAD(scad_add_cone(NULL, NULL, -1, 1, -1, NULL)); + BAD(scad_add_cone(NULL, NULL, -1, 1, -1, &geom)); + BAD(scad_add_cone(NULL, NULL, -1, 1, 2*PI, NULL)); + BAD(scad_add_cone(NULL, NULL, -1, 1, 2*PI, &geom)); + BAD(scad_add_cone(NULL, NULL, 0, -1, -1, NULL)); + BAD(scad_add_cone(NULL, NULL, 0, -1, -1, &geom)); + BAD(scad_add_cone(NULL, NULL, 0, -1, 2*PI, NULL)); + BAD(scad_add_cone(NULL, NULL, 0, -1, 2*PI, &geom)); + BAD(scad_add_cone(NULL, NULL, 0, 1, -1, NULL)); + BAD(scad_add_cone(NULL, NULL, 0, 1, -1, &geom)); + BAD(scad_add_cone(NULL, NULL, 0, 1, 2*PI, NULL)); + BAD(scad_add_cone(NULL, NULL, 0, 1, 2*PI, &geom)); + BAD(scad_add_cone(NULL, d1, -1, -1, -1, NULL)); + BAD(scad_add_cone(NULL, d1, -1, -1, -1, &geom)); + BAD(scad_add_cone(NULL, d1, -1, -1, 2*PI, NULL)); + BAD(scad_add_cone(NULL, d1, -1, -1, 2*PI, &geom)); + BAD(scad_add_cone(NULL, d1, -1, 1, -1, NULL)); + BAD(scad_add_cone(NULL, d1, -1, 1, -1, &geom)); + BAD(scad_add_cone(NULL, d1, -1, 1, 2*PI, NULL)); + BAD(scad_add_cone(NULL, d1, -1, 1, 2*PI, &geom)); + BAD(scad_add_cone(NULL, d1, 0, -1, -1, NULL)); + BAD(scad_add_cone(NULL, d1, 0, -1, -1, &geom)); + BAD(scad_add_cone(NULL, d1, 0, -1, 2*PI, NULL)); + BAD(scad_add_cone(NULL, d1, 0, -1, 2*PI, &geom)); + BAD(scad_add_cone(NULL, d1, 0, 1, -1, NULL)); + BAD(scad_add_cone(NULL, d1, 0, 1, -1, &geom)); + BAD(scad_add_cone(NULL, d1, 0, 1, 2*PI, NULL)); + BAD(scad_add_cone(NULL, d1, 0, 1, 2*PI, &geom)); + BAD(scad_add_cone(p1, NULL, -1, -1, -1, NULL)); + BAD(scad_add_cone(p1, NULL, -1, -1, -1, &geom)); + BAD(scad_add_cone(p1, NULL, -1, -1, 2*PI, NULL)); + BAD(scad_add_cone(p1, NULL, -1, -1, 2*PI, &geom)); + BAD(scad_add_cone(p1, NULL, -1, 1, -1, NULL)); + BAD(scad_add_cone(p1, NULL, -1, 1, -1, &geom)); + BAD(scad_add_cone(p1, NULL, -1, 1, 2*PI, NULL)); + BAD(scad_add_cone(p1, NULL, -1, 1, 2*PI, &geom)); + BAD(scad_add_cone(p1, NULL, 0, -1, -1, NULL)); + BAD(scad_add_cone(p1, NULL, 0, -1, -1, &geom)); + BAD(scad_add_cone(p1, NULL, 0, -1, 2*PI, NULL)); + BAD(scad_add_cone(p1, NULL, 0, -1, 2*PI, &geom)); + BAD(scad_add_cone(p1, NULL, 0, 1, -1, NULL)); + BAD(scad_add_cone(p1, NULL, 0, 1, -1, &geom)); + BAD(scad_add_cone(p1, NULL, 0, 1, 2*PI, NULL)); + BAD(scad_add_cone(p1, NULL, 0, 1, 2*PI, &geom)); + BAD(scad_add_cone(p1, d1, -1, -1, -1, NULL)); + BAD(scad_add_cone(p1, d1, -1, -1, -1, &geom)); + BAD(scad_add_cone(p1, d1, -1, -1, 2*PI, NULL)); + BAD(scad_add_cone(p1, d1, -1, -1, 2*PI, &geom)); + BAD(scad_add_cone(p1, d1, -1, 1, -1, NULL)); + BAD(scad_add_cone(p1, d1, -1, 1, -1, &geom)); + BAD(scad_add_cone(p1, d1, -1, 1, 2*PI, NULL)); + BAD(scad_add_cone(p1, d1, -1, 1, 2*PI, &geom)); + BAD(scad_add_cone(p1, d1, 0, -1, -1, NULL)); + BAD(scad_add_cone(p1, d1, 0, -1, -1, &geom)); + BAD(scad_add_cone(p1, d1, 0, -1, 2*PI, NULL)); + BAD(scad_add_cone(p1, d1, 0, -1, 2*PI, &geom)); + BAD(scad_add_cone(p1, d1, 0, 1, -1, NULL)); + BAD(scad_add_cone(p1, d1, 0, 1, -1, &geom)); + BAD(scad_add_cone(p1, d1, 0, 1, 2*PI, NULL)); + OK(scad_add_cone(p1, d1, 0, 1, 2*PI, &geom)); + OK(scad_geometry_ref_put(geom)); + BAD(scad_add_cone(p1, d1, 1, 1, 2*PI, &geom)); /* r1 == r2 */ + + OK(scad_scene_count(&c)); + CHK(c == 0); + + BAD(scad_add_torus(NULL, 0, 0, -1, NULL, NULL)); + BAD(scad_add_torus(NULL, 0, 0, -1, NULL, &geom)); + BAD(scad_add_torus(NULL, 0, 0, -1, d1, NULL)); + BAD(scad_add_torus(NULL, 0, 0, -1, d1, &geom)); + BAD(scad_add_torus(NULL, 0, 0, 2*PI, NULL, NULL)); + BAD(scad_add_torus(NULL, 0, 0, 2*PI, NULL, &geom)); + BAD(scad_add_torus(NULL, 0, 0, 2*PI, d1, NULL)); + BAD(scad_add_torus(NULL, 0, 0, 2*PI, d1, &geom)); + BAD(scad_add_torus(NULL, 0, 1, -1, NULL, NULL)); + BAD(scad_add_torus(NULL, 0, 1, -1, NULL, &geom)); + BAD(scad_add_torus(NULL, 0, 1, -1, d1, NULL)); + BAD(scad_add_torus(NULL, 0, 1, -1, d1, &geom)); + BAD(scad_add_torus(NULL, 0, 1, 2*PI, NULL, NULL)); + BAD(scad_add_torus(NULL, 0, 1, 2*PI, NULL, &geom)); + BAD(scad_add_torus(NULL, 0, 1, 2*PI, d1, NULL)); + BAD(scad_add_torus(NULL, 0, 1, 2*PI, d1, &geom)); + BAD(scad_add_torus(NULL, 1, 0, -1, NULL, NULL)); + BAD(scad_add_torus(NULL, 1, 0, -1, NULL, &geom)); + BAD(scad_add_torus(NULL, 1, 0, -1, d1, NULL)); + BAD(scad_add_torus(NULL, 1, 0, -1, d1, &geom)); + BAD(scad_add_torus(NULL, 1, 0, 2*PI, NULL, NULL)); + BAD(scad_add_torus(NULL, 1, 0, 2*PI, NULL, &geom)); + BAD(scad_add_torus(NULL, 1, 0, 2*PI, d1, NULL)); + BAD(scad_add_torus(NULL, 1, 0, 2*PI, d1, &geom)); + BAD(scad_add_torus(NULL, 1, 1, -1, NULL, NULL)); + BAD(scad_add_torus(NULL, 1, 1, -1, NULL, &geom)); + BAD(scad_add_torus(NULL, 1, 1, -1, d1, NULL)); + BAD(scad_add_torus(NULL, 1, 1, -1, d1, &geom)); + BAD(scad_add_torus(NULL, 1, 1, 2*PI, NULL, NULL)); + BAD(scad_add_torus(NULL, 1, 1, 2*PI, NULL, &geom)); + BAD(scad_add_torus(NULL, 1, 1, 2*PI, d1, NULL)); + BAD(scad_add_torus(NULL, 1, 1, 2*PI, d1, &geom)); + BAD(scad_add_torus(p1, 0, 0, -1, NULL, NULL)); + BAD(scad_add_torus(p1, 0, 0, -1, NULL, &geom)); + BAD(scad_add_torus(p1, 0, 0, -1, d1, NULL)); + BAD(scad_add_torus(p1, 0, 0, -1, d1, &geom)); + BAD(scad_add_torus(p1, 0, 0, 2*PI, NULL, NULL)); + BAD(scad_add_torus(p1, 0, 0, 2*PI, NULL, &geom)); + BAD(scad_add_torus(p1, 0, 0, 2*PI, d1, NULL)); + BAD(scad_add_torus(p1, 0, 0, 2*PI, d1, &geom)); + BAD(scad_add_torus(p1, 0, 1, -1, NULL, NULL)); + BAD(scad_add_torus(p1, 0, 1, -1, NULL, &geom)); + BAD(scad_add_torus(p1, 0, 1, -1, d1, NULL)); + BAD(scad_add_torus(p1, 0, 1, -1, d1, &geom)); + BAD(scad_add_torus(p1, 0, 1, 2*PI, NULL, NULL)); + BAD(scad_add_torus(p1, 0, 1, 2*PI, NULL, &geom)); + BAD(scad_add_torus(p1, 0, 1, 2*PI, d1, NULL)); + BAD(scad_add_torus(p1, 0, 1, 2*PI, d1, &geom)); + BAD(scad_add_torus(p1, 1, 0, -1, NULL, NULL)); + BAD(scad_add_torus(p1, 1, 0, -1, NULL, &geom)); + BAD(scad_add_torus(p1, 1, 0, -1, d1, NULL)); + BAD(scad_add_torus(p1, 1, 0, -1, d1, &geom)); + BAD(scad_add_torus(p1, 1, 0, 2*PI, NULL, NULL)); + BAD(scad_add_torus(p1, 1, 0, 2*PI, NULL, &geom)); + BAD(scad_add_torus(p1, 1, 0, 2*PI, d1, NULL)); + BAD(scad_add_torus(p1, 1, 0, 2*PI, d1, &geom)); + BAD(scad_add_torus(p1, 1, 1, -1, NULL, NULL)); + BAD(scad_add_torus(p1, 1, 1, -1, NULL, &geom)); + BAD(scad_add_torus(p1, 1, 1, -1, d1, NULL)); + BAD(scad_add_torus(p1, 1, 1, -1, d1, &geom)); + BAD(scad_add_torus(p1, 1, 1, 2*PI, NULL, NULL)); + OK(scad_add_torus(p1, 1, 1, 2*PI, NULL, &geom)); + OK(scad_geometry_ref_put(geom)); + BAD(scad_add_torus(p1, 1, 1, 2*PI, d1, NULL)); + OK(scad_add_torus(p1, 1, 1, 2*PI, d1, &geom)); + OK(scad_geometry_ref_put(geom)); + + OK(scad_scene_count(&c)); + CHK(c == 0); + + OK(scad_add_sphere(p1, 1, &geom1)); + BAD(scad_geometry_dilate(NULL, NULL, NULL, NULL)); + BAD(scad_geometry_dilate(NULL, NULL, NULL, &geom)); + BAD(scad_geometry_dilate(NULL, NULL, s, NULL)); + BAD(scad_geometry_dilate(NULL, p1, NULL, NULL)); + BAD(scad_geometry_dilate(geom1, NULL, NULL, NULL)); + BAD(scad_geometry_dilate(NULL, NULL, s, &geom)); + BAD(scad_geometry_dilate(NULL, p1, NULL, &geom)); + BAD(scad_geometry_dilate(geom1, NULL, NULL, &geom)); + BAD(scad_geometry_dilate(NULL, p1, s, NULL)); + BAD(scad_geometry_dilate(geom1, NULL, s, NULL)); + BAD(scad_geometry_dilate(NULL, p1, s, &geom)); + BAD(scad_geometry_dilate(geom1, NULL, p1, &geom)); + BAD(scad_geometry_dilate(geom1, p1, NULL, &geom)); + BAD(scad_geometry_dilate(geom1, p1, s, NULL)); + OK(scad_geometry_dilate(geom1, p1, s, &geom)); + OK(scad_geometry_ref_put(geom1)); + OK(scad_geometry_ref_put(geom)); + + OK(scad_scene_count(&c)); + CHK(c == 0); - OK(scad_add_sphere(NULL, p1, .1, &geom)); + OK(scad_add_sphere(p1, 1, &geom1)); + BAD(scad_geometry_translate(NULL, NULL, NULL)); + BAD(scad_geometry_translate(NULL, NULL, &geom)); + BAD(scad_geometry_translate(NULL, d1, NULL)); + BAD(scad_geometry_translate(geom1, NULL, NULL)); + BAD(scad_geometry_translate(NULL, d1, &geom)); + BAD(scad_geometry_translate(geom1, NULL, &geom)); + BAD(scad_geometry_translate(geom1, d1, NULL)); + OK(scad_geometry_translate(geom1, d1, &geom)); + OK(scad_geometry_ref_put(geom1)); + OK(scad_geometry_ref_put(geom)); + + OK(scad_scene_count(&c)); + CHK(c == 0); + + OK(scad_add_sphere(p1, 1, &geom1)); + BAD(scad_geometry_rotate(NULL, NULL, NULL, 1, NULL)); + BAD(scad_geometry_rotate(NULL, NULL, NULL, 1, &geom)); + BAD(scad_geometry_rotate(NULL, NULL, d1, 1, NULL)); + BAD(scad_geometry_rotate(NULL, p1, NULL, 1, NULL)); + BAD(scad_geometry_rotate(geom1, NULL, NULL, 1, NULL)); + BAD(scad_geometry_rotate(NULL, NULL, d1, 1, &geom)); + BAD(scad_geometry_rotate(NULL, p1, NULL, 1, &geom)); + BAD(scad_geometry_rotate(geom1, NULL, NULL, 1, &geom)); + BAD(scad_geometry_rotate(NULL, p1, d1, 1, NULL)); + BAD(scad_geometry_rotate(geom1, NULL, d1, 1, NULL)); + BAD(scad_geometry_rotate(geom1, p1, NULL, 1, NULL)); + BAD(scad_geometry_rotate(NULL, p1, d1, 1, &geom)); + BAD(scad_geometry_rotate(geom1, NULL, d1, 1, &geom)); + BAD(scad_geometry_rotate(geom1, p1, NULL, 1, &geom)); + BAD(scad_geometry_rotate(geom1, p1, d1, 1, NULL)); + OK(scad_geometry_rotate(geom1, p1, d1, 1, &geom)); + OK(scad_geometry_ref_put(geom1)); + OK(scad_geometry_ref_put(geom)); + + OK(scad_scene_count(&c)); + CHK(c == 0); + + OK(scad_add_polygon(get_position, coord, 0, 3, &poly)); + BAD(scad_geometry_extrude(NULL, NULL, NULL)); + BAD(scad_geometry_extrude(NULL, NULL, &geom)); + BAD(scad_geometry_extrude(NULL, d1, NULL)); + BAD(scad_geometry_extrude(poly, NULL, NULL)); + BAD(scad_geometry_extrude(NULL, d1, &geom)); + BAD(scad_geometry_extrude(poly, NULL, &geom)); + BAD(scad_geometry_extrude(poly, d1, NULL)); + OK(scad_geometry_extrude(poly, d1, &geom)); + OK(scad_geometry_ref_put(poly)); + OK(scad_geometry_ref_put(geom)); + + OK(scad_scene_count(&c)); + CHK(c == 0); + + OK(scad_add_polygon(get_position, coord, 0, 3, geoms+0)); + OK(scad_geometry_translate(geoms[0], d1, geoms+1)); + OK(scad_geometries_collect(geoms, 2, &poly)); + OK(scad_geometry_ref_put(geoms[0])); + OK(scad_geometry_ref_put(geoms[1])); + OK(scad_geometry_extrude(poly, d1, &geom)); + OK(scad_geometry_ref_put(poly)); + OK(scad_geometry_ref_put(geom)); + + OK(scad_scene_count(&c)); + CHK(c == 0); + + OK(scad_add_sphere(p1, 1, &geom)); + BAD(scad_geometry_extrude(geom, d1, &geom2)); + OK(scad_geometry_ref_put(geom)); + + OK(scad_scene_count(&c)); + CHK(c == 0); + + OK(scad_add_sphere(p1, 1, geoms+0)); + OK(scad_add_sphere(p2, 2, geoms+1)); + OK(scad_geometries_collect(geoms, 2, &geom)); + OK(scad_geometry_ref_put(geoms[0])); + OK(scad_geometry_ref_put(geoms[1])); + BAD(scad_geometry_explode(NULL, NULL, NULL)); + BAD(scad_geometry_explode(NULL, NULL, &c)); + BAD(scad_geometry_explode(NULL, &geom_array, NULL)); + BAD(scad_geometry_explode(geom, NULL, NULL)); + BAD(scad_geometry_explode(NULL, &geom_array, &c)); + BAD(scad_geometry_explode(geom, NULL, &c)); + BAD(scad_geometry_explode(geom, &geom_array, NULL)); + OK(scad_geometry_explode(geom, &geom_array, &c)); + OK(scad_geometry_ref_put(geom)); + CHK(c == 2); + OK(scad_geometry_ref_put(geom_array[0])); + OK(scad_geometry_ref_put(geom_array[1])); + MEM_RM(&allocator, geom_array); + + OK(scad_scene_count(&c)); + CHK(c == 0); + + OK(scad_add_sphere(p1, 1, &geom)); BAD(scad_geometry_ref_get(NULL)); OK(scad_geometry_ref_get(geom)); BAD(scad_geometry_ref_put(NULL)); OK(scad_geometry_ref_put(geom)); OK(scad_geometry_ref_put(geom)); + OK(scad_scene_count(&c)); + CHK(c == 0); + BAD(scad_geometry_get_count(NULL, &c)); - BAD(scad_geometry_get_count(geom1, NULL)); - OK(scad_geometry_get_count(geom1, &c)); + BAD(scad_geometry_get_count(geom, NULL)); + OK(scad_add_sphere(p1, 1, &geom)); + OK(scad_geometry_get_count(geom, &c)); + CHK(c == 1); + OK(scad_geometry_ref_put(geom)); + + OK(scad_scene_count(&c)); + CHK(c == 0); + + OK(scad_add_sphere(p1, 0.1, &geom)); + OK(scad_add_sphere(p3, 0.1, &geom1)); + OK(scad_geometries_intersect(&geom, 1, &geom1, 1, &geom2)); + BAD(scad_geometry_is_empty(NULL, &e)); + BAD(scad_geometry_is_empty(geom, NULL)); + OK(scad_geometry_is_empty(geom, &e)); + CHK(e == 0); + OK(scad_geometry_is_empty(geom2, &e)); + CHK(e == 1); + OK(scad_geometry_ref_put(geom)); + OK(scad_geometry_ref_put(geom1)); + OK(scad_geometry_ref_put(geom2)); + + OK(scad_scene_count(&c)); + CHK(c == 0); + + OK(scad_add_sphere(p1, 1, &geom)); + BAD(scad_geometry_set_custom_data(NULL, NULL, NULL)); + OK(scad_geometry_set_custom_data(geom, NULL, NULL)); + OK(scad_geometry_set_custom_data(geom, NULL, (void*)&e)); + OK(scad_geometry_set_custom_data(geom, dummy_release, NULL)); + OK(scad_geometry_set_custom_data(geom, dummy_release, (void*)&e)); + OK(scad_geometry_ref_put(geom)); + + OK(scad_scene_count(&c)); + CHK(c == 0); + + OK(scad_add_sphere(p1, 1, &geom)); + BAD(scad_geometry_get_custom_data(NULL, NULL)); + BAD(scad_geometry_get_custom_data(geom, NULL)); + OK(scad_geometry_get_custom_data(geom, &data)); + CHK(data == NULL); /* NULL when unset */ + OK(scad_geometry_set_custom_data(geom, NULL, (void*)&c)); + OK(scad_geometry_get_custom_data(geom, &data)); + CHK(data == (void*)&c); + OK(scad_geometry_set_custom_data(geom, NULL, NULL)); + OK(scad_geometry_get_custom_data(geom, &data)); + CHK(data == NULL); + OK(scad_geometry_set_custom_data(geom, dummy_release, (void*)&c)); + OK(scad_geometry_get_custom_data(geom, &data)); + CHK(data == (void*)&c); + OK(scad_geometry_ref_put(geom)); + + OK(scad_scene_count(&c)); + CHK(c == 0); + + OK(scad_add_sphere(p1, 1, &geom1)); + OK(scad_add_sphere(p2, 2, &geom2)); + BAD(scad_geometry_set_name(NULL, NULL)); + OK(scad_geometry_set_name(geom1, "sphere 1")); + OK(scad_geometry_set_name(geom1, "sphere 1")); /* Same geometry: OK */ + name = "sphere 2"; + OK(scad_geometry_set_name(geom2, name)); + BAD(scad_geometry_set_name(geom2, "sphere 1")); /* Name already in use */ + OK(scad_geometry_get_name(geom2, &name2)); + CHK(0 == strcmp(name, name2)); /* Name was left unchanged */ + OK(scad_geometry_ref_put(geom1)); + OK(scad_geometry_ref_put(geom2)); + + OK(scad_scene_count(&c)); + CHK(c == 0); + + OK(scad_add_sphere(p1, 1, &geoms[0])); + OK(scad_add_sphere(p2, 2, &geoms[1])); + BAD(scad_geometries_set_name(NULL, 0, NULL, 0)); + BAD(scad_geometries_set_name(NULL, 2, NULL, 0)); + BAD(scad_geometries_set_name(geoms, 0, NULL, 0)); + OK(scad_geometries_set_name(geoms, 2, NULL, 0)); + BAD(scad_geometries_set_name(NULL, 0, "name", 0)); + BAD(scad_geometries_set_name(NULL, 2, "name", 0)); + BAD(scad_geometries_set_name(geoms, 0, "name", 0)); + OK(scad_geometries_set_name(geoms, 2, "name", 0)); + OK(scad_geometries_set_name(geoms, 2, "name", 0)); /* Same geometries: OK */ + OK(scad_geometry_get_name(geoms[1], &name)); + BAD(scad_geometries_set_name(geoms+1, 1, "name", 0)); /* Name already in use */ + OK(scad_geometry_get_name(geoms[1], &name2)); + CHK(0 == strcmp(name, name2)); /* Name was left unchanged */ + OK(scad_geometry_ref_put(geoms[0])); + OK(scad_geometry_ref_put(geoms[1])); + + OK(scad_scene_count(&c)); + CHK(c == 0); + + OK(scad_scene_count(&c)); + CHK(c == 0); + OK(scad_add_sphere(p1, 1, &geom)); + BAD(scad_geometry_get_name(NULL, &name)); + BAD(scad_geometry_get_name(geom, NULL)); + OK(scad_geometry_get_name(geom, &name)); + CHK(name == NULL); + name = "sphere 1"; + OK(scad_geometry_set_name(geom, name)); + OK(scad_geometry_get_name(geom, &name2)); + CHK(0 == strcmp(name, name2)); + OK(scad_geometry_set_name(geom, NULL)); + OK(scad_geometry_get_name(geom, &name)); + CHK(name == NULL); + OK(scad_geometry_ref_put(geom)); + + OK(scad_scene_count(&c)); + CHK(c == 0); + + OK(scad_add_sphere(p1, 1, &geom1)); + OK(scad_add_sphere(p2, 2, &geom2)); + /* Sharing content */ + OK(scad_geometries_collect(&geom1, 1, geoms+0)); + OK(scad_geometries_collect(&geom2, 1, geoms+1)); + OK(scad_geometry_set_name(geom1, "sphere 1")); + OK(scad_geometry_set_name(geom2, "sphere 2")); + BAD(scad_geometries_swap(NULL, NULL, 0, 0)); + BAD(scad_geometries_swap(NULL, NULL, 0, SCAD_SWAP_GEOMETRY)); + BAD(scad_geometries_swap(NULL, NULL, 1, 0)); + BAD(scad_geometries_swap(NULL, &geom2, 0, 0)); + BAD(scad_geometries_swap(&geom1, NULL, 0, 0)); + BAD(scad_geometries_swap(NULL, NULL, 1, SCAD_SWAP_GEOMETRY)); + BAD(scad_geometries_swap(NULL, &geom2, 0, SCAD_SWAP_GEOMETRY)); + BAD(scad_geometries_swap(&geom1, NULL, 0, SCAD_SWAP_GEOMETRY)); + BAD(scad_geometries_swap(NULL, &geom2, 1, 0)); + BAD(scad_geometries_swap(&geom1, NULL, 1, 0)); + BAD(scad_geometries_swap(&geom1, &geom2, 0, 0)); + BAD(scad_geometries_swap(NULL, &geom2, 1, SCAD_SWAP_GEOMETRY)); + BAD(scad_geometries_swap(&geom1, NULL, 1, SCAD_SWAP_GEOMETRY)); + BAD(scad_geometries_swap(&geom1, &geom2, 0, SCAD_SWAP_GEOMETRY)); + BAD(scad_geometries_swap(&geom1, &geom2, 1, 0)); + OK(scad_geometries_swap(&geom1, &geom2, 1, SCAD_SWAP_GEOMETRY)); + /* Names not swapped */ + OK(scad_geometry_get_name(geom1, &name)); + OK(scad_geometry_get_name(geom2, &name2)); + CHK(0 == strcmp(name, "sphere 1")); + CHK(0 == strcmp(name2, "sphere 2")); + /* Geometries swaped */ + OK(scad_geometry_equal(geom1, geoms[1], &e)); + CHK(e); + OK(scad_geometry_equal(geom2, geoms[0], &e)); + CHK(e); + OK(scad_geometries_swap(&geom1, &geom2, 1, SCAD_SWAP_NAME)); + /* Names swapped */ + OK(scad_geometry_get_name(geom1, &name)); + OK(scad_geometry_get_name(geom2, &name2)); + CHK(0 == strcmp(name, "sphere 2")); + CHK(0 == strcmp(name2, "sphere 1")); + /* Geometries not swaped */ + OK(scad_geometry_equal(geom1, geoms[1], &e)); + CHK(e); + OK(scad_geometry_equal(geom2, geoms[0], &e)); + CHK(e); + OK(scad_geometry_ref_put(geom1)); + OK(scad_geometry_ref_put(geom2)); + OK(scad_geometry_ref_put(geoms[0])); + OK(scad_geometry_ref_put(geoms[1])); + + OK(scad_scene_count(&c)); + CHK(c == 0); + + OK(scad_add_rectangle(p1, d2, &geom1)); + BAD(scad_geometry_get_mass(NULL, NULL)); + BAD(scad_geometry_get_mass(NULL, &m)); + BAD(scad_geometry_get_mass(geom1, NULL)); + OK(scad_geometry_get_mass(geom1, &m)); + CHK(eq_eps(m, 1, 1e-6)); /* Mass of 2D object is its surface */ + OK(scad_geometry_ref_put(geom1)); + + OK(scad_scene_count(&c)); + CHK(c == 0); + + OK(scad_add_sphere(p1, 1, &geom1)); + OK(scad_geometry_get_mass(geom1, &m)); + CHK(eq_eps(m, PI*4/3, 1e-6)); /* Mass of 3D object is its volume */ + OK(scad_geometry_ref_put(geom1)); + + OK(scad_scene_count(&c)); + CHK(c == 0); + + OK(scad_add_rectangle(p1, d2, &geom1)); + d3_add(tmp2, p1, d3_muld(tmp2, d2, 0.5)); + BAD(scad_geometry_get_centerofmass(NULL, NULL)); + BAD(scad_geometry_get_centerofmass(NULL, tmp)); + BAD(scad_geometry_get_centerofmass(geom1, NULL)); + OK(scad_geometry_get_centerofmass(geom1, tmp)); + CHK(d3_eq_eps(tmp, tmp2, 1e-6)); + OK(scad_geometry_ref_put(geom1)); + + OK(scad_scene_count(&c)); + CHK(c == 0); + + OK(scad_add_rectangle(p1, d2, geoms+0)); + OK(scad_add_rectangle(p2, d2, geoms+1)); + d3_add(tmp2, d3_muld(tmp2, d3_add(tmp2, p1, p2), 0.5), d3_muld(tmp, d2, 0.5)); + OK(scad_geometries_collect(geoms, 2, &geom1)); + OK(scad_geometry_get_centerofmass(geom1, tmp)); + CHK(d3_eq_eps(tmp, tmp2, 1e-6)); + OK(scad_geometry_ref_put(geom1)); + OK(scad_geometry_ref_put(geoms[0])); + OK(scad_geometry_ref_put(geoms[1])); + + OK(scad_scene_count(&c)); + CHK(c == 0); + + OK(scad_add_sphere(p1, 1, &geom1)); + OK(scad_geometry_get_centerofmass(geom1, tmp)); + CHK(d3_eq_eps(tmp, p1, 1e-6)); + OK(scad_geometry_ref_put(geom1)); - BAD(scad_add_rectangle(NULL, NULL, d1, &geom)); - BAD(scad_add_rectangle(NULL, p1, NULL, &geom)); - BAD(scad_add_rectangle(NULL, p1, d1, NULL)); - BAD(scad_add_rectangle("sphere 1", p1, d1, &geom)); /* Name already used */ - OK(scad_add_rectangle(NULL, p1, d1, &geom)); + OK(scad_scene_count(&c)); + CHK(c == 0); + + OK(scad_add_sphere(p1, 1, geoms+0)); + OK(scad_add_sphere(p2, 1, geoms+1)); + OK(scad_geometries_collect(geoms, 2, &geom1)); + d3_muld(tmp2, d3_add(tmp2, p1, p2), 0.5); + OK(scad_geometry_get_centerofmass(geom1, tmp)); + CHK(d3_eq_eps(tmp, tmp2, 1e-6)); + OK(scad_geometry_ref_put(geom1)); + OK(scad_geometry_ref_put(geoms[0])); + OK(scad_geometry_ref_put(geoms[1])); + + OK(scad_scene_count(&c)); + CHK(c == 0); + + OK(scad_add_rectangle(p1, d2, &geom1)); + BAD(scad_geometry_get_closest_point(NULL, NULL, NULL, NULL, NULL)); + BAD(scad_geometry_get_closest_point(NULL, NULL, NULL, &m, NULL)); + BAD(scad_geometry_get_closest_point(NULL, NULL, tmp, NULL, NULL)); + BAD(scad_geometry_get_closest_point(NULL, p3, NULL, NULL, NULL)); + BAD(scad_geometry_get_closest_point(geom1, NULL, NULL, NULL, NULL)); + BAD(scad_geometry_get_closest_point(NULL, NULL, tmp, &m, NULL)); + BAD(scad_geometry_get_closest_point(NULL, p3, NULL, &m, NULL)); + BAD(scad_geometry_get_closest_point(geom1, NULL, NULL, &m, NULL)); + BAD(scad_geometry_get_closest_point(NULL, p3, tmp, NULL, NULL)); + BAD(scad_geometry_get_closest_point(geom1, NULL, tmp, NULL, NULL)); + BAD(scad_geometry_get_closest_point(geom1, p3, NULL, NULL, NULL)); + BAD(scad_geometry_get_closest_point(geom1, p3, tmp, NULL, NULL)); + BAD(scad_geometry_get_closest_point(geom1, p3, NULL, &m, NULL)); + BAD(scad_geometry_get_closest_point(geom1, NULL, tmp, &m, NULL)); + BAD(scad_geometry_get_closest_point(NULL, p3, tmp, &m, NULL)); + OK(scad_geometry_get_closest_point(geom1, p3, tmp, &m, NULL)); + OK(scad_geometry_get_closest_point(geom1, p3, tmp, &m, &geom)); + CHK(d3_eq_eps(tmp, p3, 1e-6)); + CHK(eq_eps(m, 0, 1e-6)); + OK(scad_geometry_ref_put(geom1)); + OK(scad_geometry_ref_put(geom)); + + OK(scad_scene_count(&c)); + CHK(c == 0); + + OK(scad_add_sphere(p1, 1, &geom1)); + d3_normalize(tmp, p2); + d3_muld(tmp2, tmp, 0.9999); + OK(scad_geometry_get_closest_point(geom1, tmp2, tmp, &m, NULL)); + OK(scad_geometry_get_closest_point(geom1, tmp2, tmp, &m, &geom)); + CHK(d3_eq_eps(tmp2, tmp, 1e-3)); + CHK(eq_eps(m, d3_len(d3_sub(tmp, tmp2, tmp)), 1e-6)); + OK(scad_geometry_ref_put(geom1)); + OK(scad_geometry_ref_put(geom)); + + OK(scad_scene_count(&c)); + CHK(c == 0); + + OK(scad_add_rectangle(p1, d2, &geom1)); + BAD(scad_geometry_get_normal(NULL, NULL, NULL, NULL)); + BAD(scad_geometry_get_normal(NULL, NULL, tmp, NULL)); + BAD(scad_geometry_get_normal(NULL, p3, NULL, NULL)); + BAD(scad_geometry_get_normal(geom1, NULL, NULL, NULL)); + BAD(scad_geometry_get_normal(NULL, p3, tmp, NULL)); + BAD(scad_geometry_get_normal(geom1, NULL, tmp, NULL)); + BAD(scad_geometry_get_normal(geom1, p3, NULL, NULL)); + d3(tmp2, 0.3, 0.3, 0); + OK(scad_geometry_get_normal(geom1, tmp2, tmp, NULL)); + OK(scad_geometry_get_normal(geom1, tmp2, tmp, &geom)); + d3(tmp2, 0, 0, 1); + CHK(d3_eq_eps(tmp, tmp2, 1e-6)); + OK(scad_geometry_ref_put(geom1)); + OK(scad_geometry_ref_put(geom)); + + OK(scad_scene_count(&c)); + CHK(c == 0); + + OK(scad_add_sphere(p1, 1, &geom1)); + d3_normalize(tmp2, p2); + OK(scad_geometry_get_normal(geom1, tmp2, tmp, NULL)); + OK(scad_geometry_get_normal(geom1, tmp2, tmp, &geom)); + CHK(d3_eq_eps(tmp, tmp2, 1e-6)); + OK(scad_geometry_ref_put(geom1)); + OK(scad_geometry_ref_put(geom)); + + OK(scad_scene_count(&c)); + CHK(c == 0); + + OK(scad_add_sphere(p1, 1, &geom)); + BAD(scad_geometry_get_bounding_box(NULL, NULL, NULL)); + BAD(scad_geometry_get_bounding_box(NULL, NULL, tmp2)); + BAD(scad_geometry_get_bounding_box(NULL, tmp, NULL)); + BAD(scad_geometry_get_bounding_box(geom, NULL, NULL)); + BAD(scad_geometry_get_bounding_box(NULL, tmp, tmp2)); + BAD(scad_geometry_get_bounding_box(geom, NULL, tmp2)); + BAD(scad_geometry_get_bounding_box(geom, tmp, NULL)); + OK(scad_geometry_get_bounding_box(geom, tmp, tmp2)); + CHK(d3_eq_eps(d3_muld(tmp, tmp, -1), tmp2, 1e-6)); + d3(tmp, 1,1,1); + CHK(d3_eq_eps(tmp, tmp2, 1e-6)); + OK(scad_geometry_ref_put(geom)); + + OK(scad_scene_count(&c)); + CHK(c == 0); + + OK(scad_add_sphere(p1, 1, &geom)); + OK(scad_geometries_collect(&geom, 1, &geom2)); + BAD(scad_geometry_equal(NULL, NULL, NULL)); + BAD(scad_geometry_equal(NULL, NULL, &e)); + BAD(scad_geometry_equal(NULL, geom2, NULL)); + BAD(scad_geometry_equal(geom, NULL, NULL)); + BAD(scad_geometry_equal(NULL, geom2, &e)); + BAD(scad_geometry_equal(geom, NULL, &e)); + BAD(scad_geometry_equal(geom, geom2, NULL)); + OK(scad_geometry_equal(geom, geom2, &e)); + CHK(e == 1); + OK(scad_geometry_ref_put(geom2)); + OK(scad_add_sphere(p1, 1, &geom2)); + OK(scad_geometry_equal(geom, geom2, &e)); + CHK(e == 0); + OK(scad_geometry_ref_put(geom)); + OK(scad_geometry_ref_put(geom2)); + + OK(scad_scene_count(&c)); + CHK(c == 0); + + OK(scad_add_sphere(p1, 1, &geom)); + OK(scad_geometries_collect(&geom, 1, &geom2)); + BAD(scad_geometry_is_included(NULL, NULL, 0, NULL)); + BAD(scad_geometry_is_included(NULL, NULL, 0, &e)); + BAD(scad_geometry_is_included(NULL, NULL, 1, NULL)); + BAD(scad_geometry_is_included(NULL, &geom2, 0, NULL)); + BAD(scad_geometry_is_included(geom, NULL, 0, NULL)); + BAD(scad_geometry_is_included(NULL, NULL, 1, &e)); + BAD(scad_geometry_is_included(NULL, &geom2, 0, &e)); + BAD(scad_geometry_is_included(geom, NULL, 0, &e)); + BAD(scad_geometry_is_included(NULL, &geom2, 1, NULL)); + BAD(scad_geometry_is_included(geom, NULL, 1, NULL)); + BAD(scad_geometry_is_included(geom, &geom2, 0, NULL)); + BAD(scad_geometry_is_included(geom, &geom2, 1, NULL)); + BAD(scad_geometry_is_included(geom, &geom2, 0, &e)); + BAD(scad_geometry_is_included(geom, NULL, 1, &e)); + BAD(scad_geometry_is_included(NULL, &geom2, 1, &e)); + OK(scad_geometry_is_included(geom, &geom2, 1, &e)); + CHK(e == 1); + OK(scad_geometry_ref_put(geom2)); + OK(scad_add_sphere(p1, 1, &geom2)); + OK(scad_geometry_is_included(geom, &geom2, 1, &e)); + CHK(e == 0); OK(scad_geometry_ref_put(geom)); + OK(scad_geometry_ref_put(geom2)); - BAD(scad_add_disk(NULL, NULL, 1, &geom)); - BAD(scad_add_disk(NULL, p1, 0, &geom)); - BAD(scad_add_disk(NULL, p1, 1, NULL)); - OK(scad_add_disk(NULL, p1, 1, &geom)); + OK(scad_scene_count(&c)); + CHK(c == 0); + + OK(scad_add_sphere(p1, 1, &geom)); + BAD(scad_geometries_collect(NULL, 0, NULL)); + BAD(scad_geometries_collect(NULL, 0, &geom2)); + BAD(scad_geometries_collect(NULL, 1, NULL)); + BAD(scad_geometries_collect(&geom, 0, NULL)); + BAD(scad_geometries_collect(NULL, 1, &geom2)); + BAD(scad_geometries_collect(&geom, 0, &geom2)); + BAD(scad_geometries_collect(&geom, 1, NULL)); + OK(scad_geometries_collect(&geom, 1, &geom2)); OK(scad_geometry_ref_put(geom)); + OK(scad_geometry_ref_put(geom2)); - BAD(scad_add_polygon(NULL, NULL, coord, 0, 3, &poly)); - BAD(scad_add_polygon(NULL, get_position, coord, 0, 2, &poly)); - BAD(scad_add_polygon(NULL, get_position, coord, 0, 2, &poly)); - BAD(scad_add_polygon(NULL, get_position, coord, 0, 3, NULL)); - OK(scad_add_polygon(NULL, get_position, coord, 0, 3, &poly)); + OK(scad_scene_count(&c)); + CHK(c == 0); - BAD(scad_add_box(NULL, NULL, d1, &geom)); - BAD(scad_add_box(NULL, p1, NULL, &geom)); - BAD(scad_add_box(NULL, p1, d1, NULL)); - OK(scad_add_box(NULL, p1, d1, &geom)); + OK(scad_add_box(p1, d0, &geom1)); + OK(scad_add_box(p2, d0, &geom2)); + BAD(scad_geometries_fuse(NULL, 0, NULL, 0, NULL)); + BAD(scad_geometries_fuse(NULL, 0, NULL, 0, &geom)); + BAD(scad_geometries_fuse(NULL, 0, NULL, 1, NULL)); + BAD(scad_geometries_fuse(NULL, 0, &geom2, 0, NULL)); + BAD(scad_geometries_fuse(NULL, 1, NULL, 0, NULL)); + BAD(scad_geometries_fuse(&geom1, 0, NULL, 0, NULL)); + BAD(scad_geometries_fuse(NULL, 0, NULL, 1, &geom)); + BAD(scad_geometries_fuse(NULL, 0, &geom2, 0, &geom)); + BAD(scad_geometries_fuse(NULL, 1, NULL, 0, &geom)); + BAD(scad_geometries_fuse(&geom1, 0, NULL, 0, &geom)); + BAD(scad_geometries_fuse(NULL, 0, &geom2, 1, NULL)); + BAD(scad_geometries_fuse(NULL, 1, NULL, 1, NULL)); + BAD(scad_geometries_fuse(&geom1, 0, NULL, 1, NULL)); + BAD(scad_geometries_fuse(NULL, 1, &geom2, 0, NULL)); + BAD(scad_geometries_fuse(&geom1, 0, &geom2, 0, NULL)); + BAD(scad_geometries_fuse(&geom1, 1, NULL, 0, NULL)); + BAD(scad_geometries_fuse(NULL, 0, &geom2, 1, &geom)); + BAD(scad_geometries_fuse(NULL, 1, NULL, 1, &geom)); + BAD(scad_geometries_fuse(&geom1, 0, NULL, 1, &geom)); + BAD(scad_geometries_fuse(NULL, 1, &geom2, 0, &geom)); + BAD(scad_geometries_fuse(&geom, 0, &geom2, 0, &geom)); + BAD(scad_geometries_fuse(&geom, 1, NULL, 0, &geom)); + BAD(scad_geometries_fuse(NULL, 1, &geom2, 1, NULL)); + BAD(scad_geometries_fuse(&geom, 0, &geom2, 1, NULL)); + BAD(scad_geometries_fuse(&geom1, 1, NULL, 1, &geom)); + BAD(scad_geometries_fuse(&geom, 1, &geom2, 0, NULL)); + BAD(scad_geometries_fuse(&geom1, 1, &geom2, 1, NULL)); + BAD(scad_geometries_fuse(&geom1, 1, &geom2, 0, &geom)); + BAD(scad_geometries_fuse(&geom1, 1, NULL, 1, &geom)); + BAD(scad_geometries_fuse(&geom1, 0, &geom2, 1, &geom)); + BAD(scad_geometries_fuse(NULL, 1, &geom2, 1, &geom)); + OK(scad_geometries_fuse(&geom1, 1, &geom2, 1, &geom)); /* disjoint geometries */ + OK(scad_geometry_get_count(geom, &c)); + CHK(c == 2); + OK(scad_geometry_ref_put(geom1)); + OK(scad_geometry_ref_put(geom2)); OK(scad_geometry_ref_put(geom)); - BAD(scad_add_cylinder(NULL, NULL, d1, 2, 1, &geom)); - BAD(scad_add_cylinder(NULL, p1, NULL, 2, 1, &geom)); - BAD(scad_add_cylinder(NULL, p1, d1, 0, 1, &geom)); - BAD(scad_add_cylinder(NULL, p1, d1, 2, -1, &geom)); - BAD(scad_add_cylinder(NULL, p1, d1, 2, 3*PI, &geom)); - BAD(scad_add_cylinder(NULL, p1, d1, 2, 1, NULL)); - OK(scad_add_cylinder(NULL, p1, d1, 2, 1, &geom)); + OK(scad_scene_count(&c)); + CHK(c == 0); + + OK(scad_add_box(p1, d1, &geom1)); + OK(scad_add_box(p2, d1, &geom2)); + OK(scad_geometries_fuse(&geom1, 1, &geom2, 1, &geom)); /* non-disjoint geometries */ + OK(scad_geometry_get_count(geom, &c)); + CHK(c == 1); + OK(scad_geometry_ref_put(geom1)); + OK(scad_geometry_ref_put(geom2)); OK(scad_geometry_ref_put(geom)); - BAD(scad_add_sphere(NULL, NULL, 1, &geom)); - BAD(scad_add_sphere(NULL, p1, 0, &geom)); - BAD(scad_add_sphere(NULL, p1, 1, NULL)); - OK(scad_add_sphere(NULL, p1, 1, &geom)); + OK(scad_scene_count(&c)); + CHK(c == 0); + + OK(scad_add_box(p1, d0, &geom1)); + OK(scad_add_box(p2, d0, &geom2)); + BAD(scad_geometries_cut(NULL, 0, NULL, 0, &geom)); + BAD(scad_geometries_cut(NULL, 0, NULL, 1, NULL)); + BAD(scad_geometries_cut(NULL, 0, &geom2, 0, NULL)); + BAD(scad_geometries_cut(NULL, 1, NULL, 0, NULL)); + BAD(scad_geometries_cut(&geom1, 0, NULL, 0, NULL)); + BAD(scad_geometries_cut(NULL, 0, NULL, 1, &geom)); + BAD(scad_geometries_cut(NULL, 0, &geom2, 0, &geom)); + BAD(scad_geometries_cut(NULL, 1, NULL, 0, &geom)); + BAD(scad_geometries_cut(&geom1, 0, NULL, 0, &geom)); + BAD(scad_geometries_cut(NULL, 0, &geom2, 1, NULL)); + BAD(scad_geometries_cut(NULL, 1, NULL, 1, NULL)); + BAD(scad_geometries_cut(&geom1, 0, NULL, 1, NULL)); + BAD(scad_geometries_cut(NULL, 1, &geom2, 0, NULL)); + BAD(scad_geometries_cut(&geom1, 0, &geom2, 0, NULL)); + BAD(scad_geometries_cut(&geom1, 1, NULL, 0, NULL)); + BAD(scad_geometries_cut(NULL, 0, &geom2, 1, &geom)); + BAD(scad_geometries_cut(NULL, 1, NULL, 1, &geom)); + BAD(scad_geometries_cut(&geom1, 0, NULL, 1, &geom)); + BAD(scad_geometries_cut(NULL, 1, &geom2, 0, &geom)); + BAD(scad_geometries_cut(&geom, 0, &geom2, 0, &geom)); + BAD(scad_geometries_cut(&geom, 1, NULL, 0, &geom)); + BAD(scad_geometries_cut(NULL, 1, &geom2, 1, NULL)); + BAD(scad_geometries_cut(&geom, 0, &geom2, 1, NULL)); + BAD(scad_geometries_cut(&geom1, 1, NULL, 1, &geom)); + BAD(scad_geometries_cut(&geom, 1, &geom2, 0, NULL)); + BAD(scad_geometries_cut(&geom1, 1, &geom2, 1, NULL)); + BAD(scad_geometries_cut(&geom1, 1, &geom2, 0, &geom)); + BAD(scad_geometries_cut(&geom1, 1, NULL, 1, &geom)); + BAD(scad_geometries_cut(&geom1, 0, &geom2, 1, &geom)); + BAD(scad_geometries_cut(NULL, 1, &geom2, 1, &geom)); + OK(scad_geometries_cut(&geom1, 1, &geom2, 1, &geom)); /* disjoint geometries */ + OK(scad_geometry_is_empty(geom, &e)); + CHK(e == 0); + OK(scad_geometry_ref_put(geom1)); + OK(scad_geometry_ref_put(geom2)); OK(scad_geometry_ref_put(geom)); - /* BAD(scad_fuse_geometries(NULL, NULL, 0, geoms, 2, &geom)); */ - BAD(scad_fuse_geometries(NULL, geoms, 2, NULL, 0, &geom)); - BAD(scad_fuse_geometries(NULL, NULL, 1, &geom2, 1, &geom)); - BAD(scad_fuse_geometries(NULL, &geom1, 1, NULL, 1, &geom)); - BAD(scad_fuse_geometries(NULL, &geom1, 1, &geom2, 1, NULL)); - OK(scad_fuse_geometries(NULL, &geom1, 1, &geom2, 1, &geom)); + OK(scad_scene_count(&c)); + CHK(c == 0); + + OK(scad_add_box(p1, d1, &geom1)); + OK(scad_add_box(p2, d1, &geom2)); + OK(scad_geometries_cut(&geom1, 1, &geom2, 1, &geom)); /* non-disjoint geometries */ + OK(scad_geometry_is_empty(geom, &e)); + CHK(e == 0); + OK(scad_geometry_ref_put(geom1)); + OK(scad_geometry_ref_put(geom2)); OK(scad_geometry_ref_put(geom)); - /* BAD(scad_cut_geometries(NULL, NULL, 0, geoms, 2, &geom)); */ - BAD(scad_cut_geometries(NULL, geoms, 2, NULL, 0, &geom)); - BAD(scad_cut_geometries(NULL, NULL, 1, &geom2, 1, &geom)); - BAD(scad_cut_geometries(NULL, &geom1, 1, NULL, 1, &geom)); - BAD(scad_cut_geometries(NULL, &geom1, 1, &geom2, 1, NULL)); - OK(scad_cut_geometries(NULL, &geom1, 1, &geom2, 1, &geom)); + OK(scad_scene_count(&c)); + CHK(c == 0); + + OK(scad_add_box(p1, d1, &geom1)); + OK(scad_geometries_cut(&geom1, 1, &geom1, 1, &geom)); + OK(scad_geometry_is_empty(geom, &e)); + CHK(e == 1); + OK(scad_geometry_ref_put(geom1)); OK(scad_geometry_ref_put(geom)); - /* BAD(scad_intersect_geometries(NULL, NULL, 0, geoms, 2, &geom)); */ - BAD(scad_intersect_geometries(NULL, geoms, 2, NULL, 0, &geom)); - BAD(scad_intersect_geometries(NULL, NULL, 1, &geom2, 1, &geom)); - BAD(scad_intersect_geometries(NULL, &geom1, 1, NULL, 1, &geom)); - BAD(scad_intersect_geometries(NULL, &geom1, 1, &geom2, 1, NULL)); - OK(scad_intersect_geometries(NULL, &geom1, 1, &geom2, 1, &geom)); + OK(scad_scene_count(&c)); + CHK(c == 0); + + OK(scad_add_sphere(p1, 1, &geom1)); + OK(scad_geometries_cut(&geom1, 1, &geom1, 1, &geom)); + OK(scad_geometry_is_empty(geom, &e)); + CHK(e == 1); + OK(scad_geometry_ref_put(geom1)); OK(scad_geometry_ref_put(geom)); - /* BAD(scad_geometries_common_boundaries(NULL, NULL, 0, geoms, 2, &geom)); */ - BAD(scad_geometries_common_boundaries(NULL, geoms, 2, NULL, 0, &geom)); - BAD(scad_geometries_common_boundaries(NULL, NULL, 1, &geom2, 1, &geom)); - BAD(scad_geometries_common_boundaries(NULL, &geom1, 1, NULL, 1, &geom)); - BAD(scad_geometries_common_boundaries(NULL, &geom1, 1, &geom2, 1, NULL)); - OK(scad_geometries_common_boundaries(NULL, &geom1, 1, &geom2, 1, &geom)); + OK(scad_scene_count(&c)); + CHK(c == 0); + + OK(scad_add_box(p1, d0, &geom1)); + OK(scad_add_box(p2, d0, &geom2)); + BAD(scad_geometries_intersect(NULL, 0, NULL, 0, &geom)); + BAD(scad_geometries_intersect(NULL, 0, NULL, 1, NULL)); + BAD(scad_geometries_intersect(NULL, 0, &geom2, 0, NULL)); + BAD(scad_geometries_intersect(NULL, 1, NULL, 0, NULL)); + BAD(scad_geometries_intersect(&geom1, 0, NULL, 0, NULL)); + BAD(scad_geometries_intersect(NULL, 0, NULL, 1, &geom)); + BAD(scad_geometries_intersect(NULL, 0, &geom2, 0, &geom)); + BAD(scad_geometries_intersect(NULL, 1, NULL, 0, &geom)); + BAD(scad_geometries_intersect(&geom1, 0, NULL, 0, &geom)); + BAD(scad_geometries_intersect(NULL, 0, &geom2, 1, NULL)); + BAD(scad_geometries_intersect(NULL, 1, NULL, 1, NULL)); + BAD(scad_geometries_intersect(&geom1, 0, NULL, 1, NULL)); + BAD(scad_geometries_intersect(NULL, 1, &geom2, 0, NULL)); + BAD(scad_geometries_intersect(&geom1, 0, &geom2, 0, NULL)); + BAD(scad_geometries_intersect(&geom1, 1, NULL, 0, NULL)); + BAD(scad_geometries_intersect(NULL, 0, &geom2, 1, &geom)); + BAD(scad_geometries_intersect(NULL, 1, NULL, 1, &geom)); + BAD(scad_geometries_intersect(&geom1, 0, NULL, 1, &geom)); + BAD(scad_geometries_intersect(NULL, 1, &geom2, 0, &geom)); + BAD(scad_geometries_intersect(&geom, 0, &geom2, 0, &geom)); + BAD(scad_geometries_intersect(&geom, 1, NULL, 0, &geom)); + BAD(scad_geometries_intersect(NULL, 1, &geom2, 1, NULL)); + BAD(scad_geometries_intersect(&geom, 0, &geom2, 1, NULL)); + BAD(scad_geometries_intersect(&geom1, 1, NULL, 1, &geom)); + BAD(scad_geometries_intersect(&geom, 1, &geom2, 0, NULL)); + BAD(scad_geometries_intersect(&geom1, 1, &geom2, 1, NULL)); + BAD(scad_geometries_intersect(&geom1, 1, &geom2, 0, &geom)); + BAD(scad_geometries_intersect(&geom1, 1, NULL, 1, &geom)); + BAD(scad_geometries_intersect(&geom1, 0, &geom2, 1, &geom)); + BAD(scad_geometries_intersect(NULL, 1, &geom2, 1, &geom)); + OK(scad_geometries_intersect(&geom1, 1, &geom2, 1, &geom)); + OK(scad_geometry_is_empty(geom, &e)); + CHK(e == 1); + OK(scad_geometry_ref_put(geom1)); + OK(scad_geometry_ref_put(geom2)); OK(scad_geometry_ref_put(geom)); - BAD(scad_geometries_partition(NULL, 2, 1, out_geoms)); - BAD(scad_geometries_partition(geoms, 0, 1, out_geoms)); - BAD(scad_geometries_partition(geoms, 2, 1, NULL)); - OK(scad_geometries_partition(geoms, 2, 1, out_geoms)); + OK(scad_scene_count(&c)); + CHK(c == 0); + + OK(scad_add_box(p1, d1, &geom1)); + OK(scad_add_box(p2, d1, &geom2)); + OK(scad_geometries_intersect(&geom1, 1, &geom2, 1, &geom)); + OK(scad_geometry_is_empty(geom, &e)); + CHK(e == 0); + OK(scad_geometry_ref_put(geom1)); + OK(scad_geometry_ref_put(geom2)); + OK(scad_geometry_ref_put(geom)); + + OK(scad_scene_count(&c)); + CHK(c == 0); + + OK(scad_add_sphere(p1, 0.1, &geom)); + OK(scad_add_sphere(p3, 0.1, &geom1)); + OK(scad_geometries_intersect(&geom, 1, &geom1, 1, &geom2)); + OK(scad_geometry_is_empty(geom2, &e)); + CHK(e == 1); + OK(scad_geometry_ref_put(geom)); + OK(scad_geometry_ref_put(geom1)); + OK(scad_geometry_ref_put(geom2)); + + OK(scad_add_box(p1, d0, geoms+0)); + OK(scad_add_box(p2, d0, geoms+1)); + BAD(scad_geometries_partition(NULL, 0, 9, NULL)); + BAD(scad_geometries_partition(NULL, 0, 9, out_geoms)); + BAD(scad_geometries_partition(NULL, 0, 0, NULL)); + BAD(scad_geometries_partition(NULL, 2, 9, NULL)); + BAD(scad_geometries_partition(geoms, 0, 9, NULL)); + BAD(scad_geometries_partition(NULL, 0, 0, out_geoms)); + BAD(scad_geometries_partition(NULL, 2, 9, out_geoms)); + BAD(scad_geometries_partition(geoms, 0, 9, out_geoms)); + BAD(scad_geometries_partition(NULL, 2, 0, NULL)); + BAD(scad_geometries_partition(geoms, 0, 0, NULL)); + BAD(scad_geometries_partition(geoms, 2, 9, NULL)); + BAD(scad_geometries_partition(geoms, 2, 0, NULL)); + BAD(scad_geometries_partition(geoms, 2, 9, out_geoms)); + BAD(scad_geometries_partition(geoms, 0, 0, out_geoms)); + BAD(scad_geometries_partition(NULL, 2, 0, out_geoms)); + OK(scad_geometries_partition(geoms, 2, SCAD_ALLOW_OVERLAPPING, out_geoms)); + OK(scad_geometry_equal(geoms[0], out_geoms[0], &e)); + CHK(e == 1); + OK(scad_geometry_equal(geoms[1], out_geoms[1], &e)); + CHK(e == 1); + OK(scad_geometry_ref_put(geoms[0])); + OK(scad_geometry_ref_put(geoms[1])); OK(scad_geometry_ref_put(out_geoms[0])); OK(scad_geometry_ref_put(out_geoms[1])); - BAD(scad_geometry_boundary(NULL, NULL, 0, &geom)); - BAD(scad_geometry_boundary(NULL, &geom1, 1, NULL)); - OK(scad_geometry_boundary(NULL, &geom1, 1, &geom)); - OK(scad_geometry_ref_put(geom)); - - BAD(scad_geometry_copy(NULL, NULL, &geom)); - BAD(scad_geometry_copy(geom1, NULL, NULL)); - BAD(scad_geometry_copy(geom1, "sphere 1", NULL)); /* Name already in use */ - OK(scad_geometry_copy(geom1, "Sphere 1", &geom)); - OK(scad_geometry_ref_put(geom)); - - BAD(scad_geometry_rename(NULL, NULL)); - BAD(scad_geometry_rename(geom2, "sphere 1")); /* Name already in use */ - OK(scad_geometry_rename(geom2, NULL)); - - BAD(scad_geometry_translate(NULL, 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, 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)); - BAD(scad_geometry_extrude(poly, NULL, d1, NULL)); - OK(scad_geometry_extrude(poly, NULL, d1, &geom)); - OK(scad_geometry_ref_put(poly)); + OK(scad_scene_count(&c)); + CHK(c == 0); + + OK(scad_add_box(p1, d1, geoms+0)); + OK(scad_add_box(p2, d1, geoms+1)); + BAD(scad_geometries_partition(geoms, 2, SCAD_DUMP_ON_OVERLAPPING_ERROR, out_geoms)); + OK(scad_geometries_partition(geoms, 2, SCAD_ALLOW_OVERLAPPING, out_geoms)); + OK(scad_geometry_ref_put(geoms[0])); + OK(scad_geometry_ref_put(geoms[1])); + OK(scad_geometry_ref_put(out_geoms[0])); + OK(scad_geometry_ref_put(out_geoms[1])); + + OK(scad_scene_count(&c)); + CHK(c == 0); + + OK(scad_add_sphere(p1, 0.1, &geoms[0])); + OK(scad_add_sphere(p3, 0.1, &geoms[1])); + OK(scad_geometries_partition(geoms, 2, SCAD_ALLOW_OVERLAPPING, out_geoms)); + OK(scad_geometries_swap(geoms, out_geoms, 2, SCAD_SWAP_GEOMETRY)); + OK(scad_geometry_ref_put(out_geoms[0])); + OK(scad_geometry_ref_put(out_geoms[1])); + BAD(scad_geometries_common_boundaries(geoms, 2, NULL, 0, &geom_array, &c)); + BAD(scad_geometries_common_boundaries(NULL, 1, geoms+1, 1, &geom_array, &c)); + BAD(scad_geometries_common_boundaries(geoms, 1, NULL, 1, &geom_array, &c)); + BAD(scad_geometries_common_boundaries(geoms, 1, geoms+1, 1, &geom_array, NULL)); + BAD(scad_geometries_common_boundaries(geoms, 1, geoms+1, 1, NULL, &c)); + OK(scad_geometries_common_boundaries(geoms, 1, geoms+1, 1, &geom_array, &c)); + CHK(c == 0); + MEM_RM(&allocator, geom_array); + OK(scad_geometry_ref_put(geoms[0])); + OK(scad_geometry_ref_put(geoms[1])); + + OK(scad_scene_count(&c)); + CHK(c == 0); + + OK(scad_add_box(p1, d1, &geoms[0])); + OK(scad_add_box(p3, d1, &geoms[1])); + OK(scad_geometries_partition(geoms, 2, SCAD_ALLOW_OVERLAPPING, out_geoms)); + OK(scad_geometries_swap(geoms, out_geoms, 2, SCAD_SWAP_GEOMETRY)); + OK(scad_geometry_ref_put(out_geoms[0])); + OK(scad_geometry_ref_put(out_geoms[1])); + OK(scad_geometries_common_boundaries(geoms+0, 1, geoms+1, 1, &geom_array, &c)); + CHK(c == 4); + for(i = 0; i < c; i++) { + OK(scad_geometry_ref_put(geom_array[i])); + } + MEM_RM(&allocator, geom_array); + OK(scad_geometry_ref_put(geoms[0])); + OK(scad_geometry_ref_put(geoms[1])); + + OK(scad_scene_count(&c)); + CHK(c == 0); + + OK(scad_add_box(p1, d1, &geoms[0])); + OK(scad_add_box(p4, d1, &geoms[1])); + OK(scad_geometries_partition(geoms, 2, SCAD_ALLOW_OVERLAPPING, out_geoms)); + OK(scad_geometries_swap(geoms, out_geoms, 2, SCAD_SWAP_GEOMETRY)); + OK(scad_geometry_ref_put(out_geoms[0])); + OK(scad_geometry_ref_put(out_geoms[1])); + OK(scad_geometries_common_boundaries(geoms+0, 1, geoms+1, 1, &geom_array, &c)); + CHK(c == 1); + for(i = 0; i < c; i++) { + OK(scad_geometry_ref_put(geom_array[i])); + } + MEM_RM(&allocator, geom_array); + OK(scad_geometry_ref_put(geoms[0])); + OK(scad_geometry_ref_put(geoms[1])); + + OK(scad_scene_count(&c)); + CHK(c == 0); + + OK(scad_add_sphere(p1, 0.1, &geom1)); + BAD(scad_geometries_boundaries(NULL, 0, NULL, NULL)); + BAD(scad_geometries_boundaries(NULL, 0, NULL, &c)); + BAD(scad_geometries_boundaries(NULL, 0, &geom_array, NULL)); + BAD(scad_geometries_boundaries(NULL, 1, NULL, NULL)); + BAD(scad_geometries_boundaries(&geom1, 0, NULL, NULL)); + BAD(scad_geometries_boundaries(NULL, 0, &geom_array, &c)); + BAD(scad_geometries_boundaries(NULL, 1, NULL, &c)); + BAD(scad_geometries_boundaries(&geom1, 0, NULL, &c)); + BAD(scad_geometries_boundaries(NULL, 1, &geom_array, NULL)); + BAD(scad_geometries_boundaries(&geom1, 0, &geom_array, NULL)); + BAD(scad_geometries_boundaries(&geom1, 1, NULL, NULL)); + BAD(scad_geometries_boundaries(&geom1, 1, &geom_array, NULL)); + BAD(scad_geometries_boundaries(&geom1, 1, NULL, &c)); + BAD(scad_geometries_boundaries(&geom1, 0, &geom_array, &c)); + BAD(scad_geometries_boundaries(NULL, 1, &geom_array, &c)); + OK(scad_geometries_boundaries(&geom1, 1, &geom_array, &c)); + for(i = 0; i < c; i++) { + OK(scad_geometry_ref_put(geom_array[i])); + } + MEM_RM(&allocator, geom_array); + OK(scad_geometry_ref_put(geom1)); + + OK(scad_scene_count(&c)); + CHK(c == 0); + + OK(scad_add_sphere(p1, 0.1, &geom1)); + BAD(scad_geometry_copy(NULL, &geom)); + BAD(scad_geometry_copy(geom1, NULL)); + OK(scad_geometry_copy(geom1, &geom)); OK(scad_geometry_ref_put(geom)); + OK(scad_geometry_ref_put(geom1)); - BAD(scad_scene_write(NULL)); - ERR(scad_scene_write("")); - OK(scad_scene_write("/tmp/test.step")); + OK(scad_scene_count(&c)); + CHK(c == 0); - BAD(scad_step_import(NULL, "step", &geom_array, &c)); - BAD(scad_step_import("/tmp/test.step", "step", NULL, &c)); - BAD(scad_step_import("/tmp/test.step", "step", &geom_array, NULL)); - ERR(scad_step_import("dont_exist.step", "step", &geom_array, &c)); - OK(scad_step_import("/tmp/test.step", "step", &geom_array, &c)); + OK(scad_add_sphere(p1, 0.1, &geom)); + BAD(scad_geometry_set_visibility(NULL, 1, 1)); + OK(scad_geometry_set_visibility(geom, 1, 1)); + OK(scad_geometry_ref_put(geom)); + + OK(scad_scene_count(&c)); + CHK(c == 0); + + OK(scad_add_rectangle(p1, d2, &geom1)); + OK(scad_add_rectangle(p2, d2, &geom2)); + BAD(scad_geometries_set_periodic(NULL, 0, NULL, 0, NULL)); + BAD(scad_geometries_set_periodic(NULL, 0, NULL, 1, NULL)); + BAD(scad_geometries_set_periodic(NULL, 0, &geom2, 0, NULL)); + BAD(scad_geometries_set_periodic(NULL, 1, NULL, 0, NULL)); + BAD(scad_geometries_set_periodic(&geom1, 0, NULL, 0, NULL)); + BAD(scad_geometries_set_periodic(NULL, 0, &geom2, 1, NULL)); + BAD(scad_geometries_set_periodic(NULL, 1, NULL, 1, NULL)); + BAD(scad_geometries_set_periodic(&geom1, 0, NULL, 1, NULL)); + BAD(scad_geometries_set_periodic(NULL, 1, &geom2, 0, NULL)); + BAD(scad_geometries_set_periodic(&geom1, 0, &geom2, 0, NULL)); + BAD(scad_geometries_set_periodic(&geom1, 1, NULL, 0, NULL)); + BAD(scad_geometries_set_periodic(&geom1, 1, &geom2, 0, NULL)); + BAD(scad_geometries_set_periodic(&geom1, 1, NULL, 1, NULL)); + BAD(scad_geometries_set_periodic(&geom1, 0, &geom2, 1, NULL)); + BAD(scad_geometries_set_periodic(NULL, 1, &geom2, 1, NULL)); + BAD(scad_geometries_set_periodic(&geom1, 1, &geom2, 1, NULL)); + BAD(scad_geometries_set_periodic(NULL, 0, NULL, 0, affine)); + BAD(scad_geometries_set_periodic(NULL, 0, NULL, 1, affine)); + BAD(scad_geometries_set_periodic(NULL, 0, &geom2, 0, affine)); + BAD(scad_geometries_set_periodic(NULL, 1, NULL, 0, affine)); + BAD(scad_geometries_set_periodic(&geom1, 0, NULL, 0, affine)); + BAD(scad_geometries_set_periodic(NULL, 0, &geom2, 1, affine)); + BAD(scad_geometries_set_periodic(NULL, 1, NULL, 1, affine)); + BAD(scad_geometries_set_periodic(&geom1, 0, NULL, 1, affine)); + BAD(scad_geometries_set_periodic(NULL, 1, &geom2, 0, affine)); + BAD(scad_geometries_set_periodic(&geom1, 0, &geom2, 0, affine)); + BAD(scad_geometries_set_periodic(&geom1, 1, NULL, 0, affine)); + BAD(scad_geometries_set_periodic(&geom1, 1, &geom2, 0, affine)); + BAD(scad_geometries_set_periodic(&geom1, 1, NULL, 1, affine)); + BAD(scad_geometries_set_periodic(&geom1, 0, &geom2, 1, affine)); + BAD(scad_geometries_set_periodic(NULL, 1, &geom2, 1, affine)); + OK(scad_geometries_set_periodic(&geom1, 1, &geom2, 1, affine)); + OK(scad_geometry_ref_put(geom2)); + OK(scad_add_rectangle(p4, d2, &geom2)); + ERR(scad_geometries_set_periodic(&geom1, 1, &geom2, 1, affine)); + OK(scad_geometry_ref_put(geom1)); + OK(scad_geometry_ref_put(geom2)); + + OK(scad_scene_count(&c)); + CHK(c == 0); + + OK(scad_add_sphere(p1, 0.1, &geom1)); + BAD(scad_geometries_set_mesh_size_modifier(NULL, 0, 2, -1.5)); + BAD(scad_geometries_set_mesh_size_modifier(NULL, 0, 2, 1.5)); + BAD(scad_geometries_set_mesh_size_modifier(NULL, 0, SCAD_ABSOLUTE_SIZE, -1.5)); + BAD(scad_geometries_set_mesh_size_modifier(NULL, 1, 2, -1.5)); + BAD(scad_geometries_set_mesh_size_modifier(&geom1, 0, 2, -1.5)); + BAD(scad_geometries_set_mesh_size_modifier(NULL, 0, SCAD_ABSOLUTE_SIZE, 1.5)); + BAD(scad_geometries_set_mesh_size_modifier(NULL, 1, 2, 1.5)); + BAD(scad_geometries_set_mesh_size_modifier(&geom1, 0, 2, 1.5)); + BAD(scad_geometries_set_mesh_size_modifier(NULL, 1, SCAD_ABSOLUTE_SIZE, -1.5)); + BAD(scad_geometries_set_mesh_size_modifier(&geom1, 0, SCAD_ABSOLUTE_SIZE, -1.5)); + BAD(scad_geometries_set_mesh_size_modifier(&geom1, 1, 2, -1.5)); + BAD(scad_geometries_set_mesh_size_modifier(&geom1, 1, SCAD_ABSOLUTE_SIZE, -1.5)); + BAD(scad_geometries_set_mesh_size_modifier(&geom1, 1, 2, 1.5)); + BAD(scad_geometries_set_mesh_size_modifier(&geom1, 0, SCAD_ABSOLUTE_SIZE, 1.5)); + BAD(scad_geometries_set_mesh_size_modifier(NULL, 1, SCAD_ABSOLUTE_SIZE, 1.5)); + OK(scad_geometries_set_mesh_size_modifier(&geom1, 1, SCAD_ABSOLUTE_SIZE, 1.5)); + OK(scad_geometry_ref_put(geom1)); + + OK(scad_scene_count(&c)); + CHK(c == 0); + + OK(scad_add_sphere(p1, 0.1, &geom1)); + BAD(scad_geometries_set_mesh_algorithm(NULL, 0, 0)); + BAD(scad_geometries_set_mesh_algorithm(NULL, 0, SCAD_AUTOMATIC)); + BAD(scad_geometries_set_mesh_algorithm(NULL, 1, 0)); + BAD(scad_geometries_set_mesh_algorithm(&geom1, 0, 0)); + BAD(scad_geometries_set_mesh_algorithm(NULL, 1, SCAD_AUTOMATIC)); + BAD(scad_geometries_set_mesh_algorithm(&geom1, 0, SCAD_AUTOMATIC)); + BAD(scad_geometries_set_mesh_algorithm(&geom1, 1, 0)); + OK(scad_geometries_set_mesh_algorithm(&geom1, 1, SCAD_AUTOMATIC)); + OK(scad_geometry_ref_put(geom1)); + + OK(scad_scene_count(&c)); + CHK(c == 0); + + options.Mesh.MeshSizeFromPoints = 1; + options.Mesh.MeshSizeExtendFromBoundary = 1; + OK(scad_set_options(&options)); + + OK(scad_add_box(p1, d1, &geom)); + OK(scad_scene_mesh()); + darray_double_init(&allocator, &trg); + OK(scad_stl_get_data(geom, &trg)); + c = darray_double_size_get(&trg); + darray_double_clear(&trg); + BAD(scad_geometries_clear_mesh(NULL, 0)); + BAD(scad_geometries_clear_mesh(NULL, 1)); + BAD(scad_geometries_clear_mesh(&geom, 0)); + OK(scad_geometries_clear_mesh(&geom, 1)); + OK(scad_stl_get_data(geom, &trg)); + CHK(0 == darray_double_size_get(&trg)); + OK(scad_geometries_set_mesh_size_modifier(&geom, 1, SCAD_SIZE_FACTOR, 0.5)); + OK(scad_scene_mesh()); + OK(scad_stl_get_data(geom, &trg)); + CHK(c < darray_double_size_get(&trg)); + darray_double_release(&trg); + OK(scad_geometry_ref_put(geom)); + + options.Mesh.MeshSizeFromPoints = 0; + options.Mesh.MeshSizeExtendFromBoundary = 0; + OK(scad_set_options(&options)); + + OK(scad_scene_count(&c)); + CHK(c == 0); + + OK(scad_add_box(p1, d1, &geoms[0])); + OK(scad_add_box(p4, d1, &geoms[1])); + name = "/tmp/test"LINE_STRING".step"; + OK(scad_scene_write(name)); + BAD(scad_step_import(NULL, NULL, NULL)); + BAD(scad_step_import(NULL, NULL, &c)); + BAD(scad_step_import(NULL, &geom_array, NULL)); + BAD(scad_step_import("/tmp/test.step", NULL, NULL)); + BAD(scad_step_import(NULL, &geom_array, &c)); + BAD(scad_step_import("/tmp/test.step", NULL, &c)); + BAD(scad_step_import("/tmp/test.step", &geom_array, NULL)); + ERR(scad_step_import("dont_exist.step", &geom_array, &c)); + OK(scad_step_import(name, &geom_array, &c)); + CHK(c == 2); + for(i = 0; i < 2; i++) { + OK(scad_geometries_cut(geoms+i, 1, geom_array+i, 1, &geom)); + OK(scad_geometry_is_empty(geom, &e)); + OK(scad_geometry_ref_put(geom)); + CHK(e == 1); + } for(i = 0; i < c; i++) { OK(scad_geometry_ref_put(geom_array[i])); } MEM_RM(&allocator, geom_array); + OK(scad_geometry_ref_put(geoms[0])); + OK(scad_geometry_ref_put(geoms[1])); + + OK(scad_scene_count(&c)); + CHK(c == 0); + + OK(scad_add_box(p1, d1, &geom)); + BAD(scad_stl_export(NULL, NULL, 9, 0)); + BAD(scad_stl_export(NULL, NULL, SCAD_KEEP_NORMALS_UNCHANGED, 0)); + BAD(scad_stl_export(geom, NULL, 9, 0)); + BAD(scad_stl_export(geom, NULL, SCAD_KEEP_NORMALS_UNCHANGED, 0)); + OK(scad_stl_export(geom, "/tmp/test"LINE_STRING, SCAD_FORCE_NORMALS_OUTWARD, 1)); + OK(scad_geometry_set_name(geom, "name")); + BAD(scad_stl_export(NULL, NULL, 9, 0)); + BAD(scad_stl_export(NULL, NULL, SCAD_KEEP_NORMALS_UNCHANGED, 0)); + BAD(scad_stl_export(geom, NULL, 9, 0)); + OK(scad_stl_export(geom, NULL, SCAD_KEEP_NORMALS_UNCHANGED, 0)); + OK(scad_stl_export(geom, "/tmp/test"LINE_STRING, SCAD_FORCE_NORMALS_OUTWARD, 1)); + OK(scad_geometry_ref_put(geom)); + + OK(scad_scene_count(&c)); + CHK(c == 0); + + OK(scad_add_box(p1, d1, &geoms[0])); + OK(scad_add_box(p4, d1, &geoms[1])); + OK(scad_geometries_collect(geoms, 2, &geom)); + BAD(scad_stl_export_partial(NULL, geoms, 1, NULL, 9, 0)); + BAD(scad_stl_export_partial(NULL, geoms, 1, NULL, SCAD_KEEP_NORMALS_UNCHANGED, 0)); + BAD(scad_stl_export_partial(geom, geoms, 1, NULL, 9, 0)); + BAD(scad_stl_export_partial(geom, geoms, 1, NULL, SCAD_FORCE_NORMALS_INWARD, 0)); + OK(scad_stl_export_partial(geom, geoms, 1, "/tmp/test"LINE_STRING, + SCAD_FORCE_NORMALS_OUTWARD, 1)); + OK(scad_geometry_set_name(geom, "name")); + BAD(scad_stl_export_partial(NULL, geoms, 1, NULL, 9, 0)); + BAD(scad_stl_export_partial(NULL, geoms, 1, NULL, SCAD_KEEP_NORMALS_UNCHANGED, 0)); + BAD(scad_stl_export_partial(geom, geoms, 1, NULL, 9, 0)); + OK(scad_stl_export_partial(geom, geoms, 1, NULL, SCAD_FORCE_NORMALS_INWARD, 0)); + OK(scad_stl_export_partial(geom, geoms, 1, "/tmp/test"LINE_STRING, + SCAD_FORCE_NORMALS_OUTWARD, 1)); + OK(scad_stl_export_partial(geom, NULL, 0, "/tmp/test"LINE_STRING, + SCAD_FORCE_NORMALS_OUTWARD, 1)); + OK(scad_geometry_ref_put(geom)); + OK(scad_geometry_ref_put(geoms[0])); + OK(scad_geometry_ref_put(geoms[1])); + + OK(scad_scene_count(&c)); + CHK(c == 0); + + OK(scad_add_box(p1, d1, &geom)); + BAD(scad_stl_export_split(NULL, NULL, 9, 0)); + BAD(scad_stl_export_split(NULL, NULL, SCAD_KEEP_NORMALS_UNCHANGED, 0)); + BAD(scad_stl_export_split(geom, NULL, 9, 0)); + BAD(scad_stl_export_split(geom, NULL, SCAD_KEEP_NORMALS_UNCHANGED, 0)); + OK(scad_stl_export_split(geom, "/tmp/test"LINE_STRING, SCAD_FORCE_NORMALS_OUTWARD, 1)); + OK(scad_geometry_set_name(geom, "name")); + BAD(scad_stl_export_split(NULL, NULL, 9, 0)); + BAD(scad_stl_export_split(NULL, NULL, SCAD_KEEP_NORMALS_UNCHANGED, 0)); + BAD(scad_stl_export_split(geom, NULL, 9, 0)); + OK(scad_stl_export_split(geom, NULL, SCAD_KEEP_NORMALS_UNCHANGED, 0)); + OK(scad_stl_export_split(geom, "/tmp/test"LINE_STRING, SCAD_FORCE_NORMALS_OUTWARD, 1)); + OK(scad_geometry_ref_put(geom)); + OK(scad_scene_count(&c)); + CHK(c == 0); + + OK(scad_add_box(p1, d1, &geom)); darray_double_init(&allocator, &trg); + OK(scad_geometries_set_mesh_algorithm(&geom, 1, SCAD_INITIAL_MESH_ONLY)); + OK(scad_scene_mesh()); 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, 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)); + BAD(scad_stl_get_data(NULL, &trg)); + BAD(scad_stl_get_data(geom, NULL)); + OK(scad_stl_get_data(geom, &trg)); + CHK(12 * 9 == darray_double_size_get(&trg)); darray_double_release(&trg); + OK(scad_geometry_ref_put(geom)); - 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)); + OK(scad_scene_count(&c)); + CHK(c == 0); - 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)); + OK(scad_add_box(p1, d1, &geoms[0])); + OK(scad_add_box(p4, d1, &geoms[1])); + OK(scad_geometries_collect(geoms, 2, &geom)); + darray_double_init(&allocator, &trg); + OK(scad_geometries_set_mesh_algorithm(geoms, 2, SCAD_INITIAL_MESH_ONLY)); + OK(scad_scene_mesh()); + BAD(scad_stl_get_data_partial(NULL, NULL, 0, NULL)); + BAD(scad_stl_get_data_partial(NULL, NULL, 0, &trg)); + BAD(scad_stl_get_data_partial(geom, NULL, 0, NULL)); + OK(scad_stl_get_data_partial(geom, NULL, 0, &trg)); + CHK(2 * 12 * 9 == darray_double_size_get(&trg)); + darray_double_clear(&trg); + OK(scad_stl_get_data_partial(geom, geoms, 1, &trg)); + CHK(12 * 9 == darray_double_size_get(&trg)); + darray_double_clear(&trg); + OK(scad_stl_get_data_partial(geom, geoms, 2, &trg)); + CHK(0 == darray_double_size_get(&trg)); + darray_double_release(&trg); + OK(scad_geometry_ref_put(geom)); + OK(scad_geometry_ref_put(geoms[0])); + OK(scad_geometry_ref_put(geoms[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)); + OK(scad_scene_count(&c)); + CHK(c == 0); + + OK(scad_add_box(p1, d1, &geom)); + darray_double_init(&allocator, &trg); + OK(scad_stl_get_data(geom, &trg)); + CHK(0 == darray_double_size_get(&trg)); /* No call to mesh */ + BAD(scad_stl_data_write(NULL, NULL, 9, 0)); + BAD(scad_stl_data_write(NULL, NULL, SCAD_KEEP_NORMALS_UNCHANGED, 0)); + BAD(scad_stl_data_write(&trg, NULL, 9, 0)); + BAD(scad_stl_data_write(&trg, NULL, SCAD_KEEP_NORMALS_UNCHANGED, 0)); + OK(scad_stl_data_write(&trg, "/tmp/test"LINE_STRING, SCAD_FORCE_NORMALS_OUTWARD, 1)); + OK(scad_geometry_ref_put(geom)); + + OK(scad_scene_count(&c)); + CHK(c == 0); + + options.Misc.LogRefCounting = SCAD_LOG_DIMTAGS_ONLY_UNDELETED; + OK(scad_set_options(&options)); OK(scad_finalize()); diff --git a/src/test_export.c b/src/test_export.c @@ -35,39 +35,50 @@ main(int argc, char* argv[]) struct scad_geometry* fused = NULL; struct scad_geometry* geoms[2]; struct mem_allocator allocator; + 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)); - OK(scad_add_rectangle("rectangle", p1, d1, &rectangle)); - OK(scad_add_box("cube", p1, d1, &cube)); - OK(scad_add_cylinder(NULL, p2, d1, 0.5, 2*PI, geoms+1)); + OK(scad_add_rectangle(p1, d1, &rectangle)); + OK(scad_geometry_set_name(rectangle, "rectangle")); + OK(scad_add_box(p1, d1, &cube)); + OK(scad_geometry_set_name(cube, "cube")); + OK(scad_add_cylinder(p2, d1, 0.5, 2*PI, geoms+1)); geoms[0] = cube; - OK(scad_fuse_geometries("fused", geoms, 1, geoms+1, 1, &fused)); + OK(scad_geometries_fuse(geoms, 1, geoms+1, 1, &fused)); + OK(scad_geometry_set_name(fused, "fused")); OK(scad_scene_mesh()); /* Do not define a volume */ - BAD(scad_stl_export(rectangle, "not-a-volume.stl", Scad_force_normals_outward, 0)); + 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(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, Scad_force_normals_outward, 0)); - OK(scad_stl_export(cube, "bin_cube", Scad_force_normals_outward, 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(fused, NULL, Scad_force_normals_outward, 0)); - OK(scad_stl_export(fused, "bin_fused", Scad_force_normals_outward, 1)); + OK(scad_stl_export(fused, NULL, SCAD_FORCE_NORMALS_OUTWARD, 0)); + OK(scad_stl_export(fused, "bin_fused", SCAD_FORCE_NORMALS_OUTWARD, 1)); /* New meshing algorithm */ - OK(scad_geometries_set_mesh_algorithm(geoms, 2, Scad_Quasi_Structured)); + OK(scad_geometries_set_mesh_algorithm(geoms, 2, SCAD_QUASI_STRUCTURED)); OK(scad_scene_mesh()); - OK(scad_stl_export(rectangle, "rectangle_quasi", Scad_keep_normals_unchanged, 0)); - OK(scad_stl_export(cube, "cube_quasi", Scad_force_normals_outward, 0)); - OK(scad_stl_export(fused, "fused_quasi", Scad_force_normals_outward, 0)); + OK(scad_stl_export(rectangle, "rectangle_quasi", SCAD_KEEP_NORMALS_UNCHANGED, 0)); + OK(scad_stl_export(cube, "cube_quasi", SCAD_FORCE_NORMALS_OUTWARD, 0)); + OK(scad_stl_export(fused, "fused_quasi", SCAD_FORCE_NORMALS_OUTWARD, 0)); + + OK(scad_geometry_ref_put(rectangle)); + OK(scad_geometry_ref_put(cube)); + OK(scad_geometry_ref_put(geoms[1])); + OK(scad_geometry_ref_put(fused)); OK(scad_finalize()); diff --git a/src/test_export2.c b/src/test_export2.c @@ -55,38 +55,44 @@ main(int argc, char* argv[]) struct scad_geometry* cube2 = NULL; struct scad_geometry* cube3 = NULL; struct mem_allocator allocator; + struct scad_options options = SCAD_DEFAULT_OPTIONS; (void)argc; (void)argv; OK(mem_init_proxy_allocator(&allocator, &mem_default_allocator)); darray_double_init(&allocator, &array); OK(scad_initialize(NULL, &allocator, 3)); + options.Misc.LogRefCounting = SCAD_LOG_DIMTAGS_ALL | SCAD_LOG_GEOMETRY; + OK(scad_set_options(&options)); - OK(scad_add_box("cube1", p1, d1, &cube1)); - OK(scad_add_box("cube2", p2, d2, &cube2)); - OK(scad_add_box("cube3", p3, d3, &cube3)); + OK(scad_add_box(p1, d1, &cube1)); + OK(scad_geometry_set_name(cube1, "cube1")); + OK(scad_add_box(p2, d2, &cube2)); + OK(scad_geometry_set_name(cube2, "cube2")); + OK(scad_add_box(p3, d3, &cube3)); + OK(scad_geometry_set_name(cube3, "cube3")); OK(scad_scene_mesh()); /* Check that all three cubes can be exported whith forced normals */ - OK(scad_stl_export(cube1, NULL, Scad_force_normals_outward, 0)); - OK(scad_stl_export(cube2, NULL, Scad_force_normals_outward, 0)); - OK(scad_stl_export(cube3, "bin_cube3", Scad_force_normals_outward, 1)); + OK(scad_stl_export(cube1, NULL, SCAD_FORCE_NORMALS_OUTWARD, 0)); + OK(scad_stl_export(cube2, NULL, SCAD_FORCE_NORMALS_OUTWARD, 0)); + OK(scad_stl_export(cube3, "bin_cube3", SCAD_FORCE_NORMALS_OUTWARD, 1)); /* Check that 2 cubes as a single model can be exported whith forced normals */ OK(scad_stl_get_data(cube1, &array)); OK(scad_stl_get_data(cube2, &array)); - OK(scad_stl_data_write(&array, "2cubes.stl", Scad_force_normals_outward, 0)); + OK(scad_stl_data_write(&array, "2cubes.stl", SCAD_FORCE_NORMALS_OUTWARD, 0)); /* Check that with 3 cubes as a single model, the model cannot be exported * whith forced normals... */ OK(scad_stl_get_data(cube3, &array)); - BAD(scad_stl_data_write(&array, "3cubes.stl", Scad_force_normals_outward, 0)); + BAD(scad_stl_data_write(&array, "3cubes.stl", SCAD_FORCE_NORMALS_OUTWARD, 0)); /* ...but can be exported anyway without forcing normals... */ - OK(scad_stl_data_write(&array, "3cubes.stl", Scad_keep_normals_unchanged, 0)); + OK(scad_stl_data_write(&array, "3cubes.stl", SCAD_KEEP_NORMALS_UNCHANGED, 0)); /* ...and can still be exported if some triangles are duplicated */ OK(scad_stl_get_data(cube1, &array)); - OK(scad_stl_data_write(&array, "3cubesd.stl", Scad_keep_normals_unchanged, 0)); + OK(scad_stl_data_write(&array, "3cubesd.stl", SCAD_KEEP_NORMALS_UNCHANGED, 0)); OK(scad_geometry_ref_put(cube1)); OK(scad_geometry_ref_put(cube2)); diff --git a/src/test_lifetime.c b/src/test_lifetime.c @@ -36,13 +36,14 @@ alive_and_well ASSERT(g && allocator); - OK(scad_geometry_explode(g, "alive_and_well", &geom_array, &c)); + OK(scad_geometry_explode(g, &geom_array, &c)); + OK(scad_geometries_set_name(geom_array, c, "alive_and_well", 0)); 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_extrude(geom_array[i], dir1, &gg)); OK(scad_geometry_ref_put(geom_array[i])); OK(scad_geometry_ref_put(gg)); } @@ -57,13 +58,13 @@ main(int argc, char* argv[]) 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; + struct scad_geometry** list2 = NULL; + size_t list_n, list_n2, center_n, i; double center[3], N[3]; struct scad_options options = SCAD_DEFAULT_OPTIONS; @@ -72,57 +73,72 @@ main(int argc, char* 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; + 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_add_box(p1, diago, &geom)); + OK(scad_geometries_boundaries(&geom, 1, &list, &list_n)); + OK(scad_geometries_set_name(list, list_n, "boundary", 0)); OK(scad_geometry_ref_put(geom)); - alive_and_well(geom1, &allocator); + for(i = 0; i < list_n; i++) { + alive_and_well(list[i], &allocator); + } + MEM_RM(&allocator, list); list = NULL; /* 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_add_rectangle(p1, base, &geom)); + OK(scad_geometry_extrude(geom, dir1, &geom1)); OK(scad_geometry_ref_put(geom)); - OK(scad_geometry_boundary("boundary", &geom1, 1, &geom2)); + OK(scad_geometries_boundaries(&geom1, 1, &list, &list_n)); + OK(scad_geometries_set_name(list, list_n, "boundary", 0)); OK(scad_geometry_ref_put(geom1)); - alive_and_well(geom2, &allocator); + for(i = 0; i < list_n; i++) { + alive_and_well(list[i], &allocator); + } + MEM_RM(&allocator, list); list = NULL; /* 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_add_rectangle(p1, base, &geom)); + OK(scad_geometry_extrude(geom, 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], &center_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)); + OK(scad_add_box(p1, diago, &geom1)); + OK(scad_geometries_boundaries(&geom1, 1, &list, &list_n)); + OK(scad_geometries_set_name(list, list_n, "bcavity", 0)); + for(i = 0; i < list_n; i++) { + OK(scad_geometry_get_count(list[i], &center_n)); + ASSERT(center_n == 1); + OK(scad_geometry_get_centerofmass(list[i], center)); + OK(scad_geometry_get_normal(list[i], center, N, &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); + MEM_RM(&allocator, list); list = NULL; 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_add_box(p1, diago, geoms+0)); + OK(scad_add_box(p1, diago_, geoms+1)); + OK(scad_geometries_boundaries(geoms+0, 1, &list, &list_n)); + OK(scad_geometries_boundaries(geoms+1, 1, &list2, &list_n2)); 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); + for(i = 0; i < list_n; i++) { + alive_and_well(list[i], &allocator); + } + for(i = 0; i < list_n2; i++) { + alive_and_well(list2[i], &allocator); + } OK(scad_geometry_ref_put(out_geoms[0])); OK(scad_geometry_ref_put(out_geoms[1])); + MEM_RM(&allocator, list); list = NULL; + MEM_RM(&allocator, list2); list = NULL; OK(scad_finalize()); diff --git a/src/test_normals.c b/src/test_normals.c @@ -0,0 +1,97 @@ +/* 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/dynamic_array_double.h> + +#include <stdlib.h> +#include <stdio.h> + +int +main(int argc, char* argv[]) +{ + res_T res = RES_OK; + double p1[3] = {0, 0, 0}; + double s1[3] = {1, 1, 0}; + double c[3] = {0.5, 0.5, 0}; + double scale[3] = {0.5, 0.5, 0.5}; + double d1[3] = {0, 0, 1}; + double d1_[3] = {0, 0, -1}; + double m1, m2; + struct scad_geometry* base = NULL; + struct scad_geometry* sbase = NULL; + struct scad_geometry* geom1 = NULL; + struct scad_geometry* geom2 = NULL; + struct scad_geometry* geoms[2]; + struct scad_geometry* part[2]; + struct mem_allocator allocator; + struct scad_options options = SCAD_DEFAULT_OPTIONS; + struct darray_double trg; + + (void)argc; (void)argv; + + OK(mem_init_proxy_allocator(&allocator, &mem_default_allocator)); + darray_double_init(&allocator, &trg); + OK(scad_initialize(NULL, &allocator, 3)); + OK(scad_set_options(NULL)); + OK(scad_set_options(&options)); + + OK(scad_add_rectangle("base", p1, s1, &base)); + OK(scad_geometry_extrude(base, "top", d1, &geom1)); + OK(scad_geometry_get_mass(geom1, &m1)); + + OK(scad_geometry_dilate(base, c, scale, "small_base", &sbase)); + OK(scad_geometry_extrude(sbase, "bottom", d1_, &geom2)); + OK(scad_geometry_get_mass(geom2, &m2)); + + geoms[0] = geom1; + geoms[1] = geom2; + OK(scad_synchronize()); + OK(scad_geometries_partition(geoms, 2, 0, part)); + OK(scad_synchronize()); + OK(scad_scene_mesh()); + OK(scad_synchronize()); + + CHK(eq_eps(m1, 1, 1e-10)); + CHK(eq_eps(m2, 0.25, 1e-10)); + + OK(scad_stl_export(part[0], "/tmp/geom1", SCAD_FORCE_NORMALS_OUTWARD, 0)); + OK(scad_stl_export(part[1], "/tmp/geom2", SCAD_FORCE_NORMALS_OUTWARD, 0)); + + OK(scad_stl_get_data(geom2, &trg)); + OK(scad_stl_data_write(&trg, "/tmp/test", 0, 0)); + OK(scad_stl_data_write(&trg, "/tmp/test", 1, 1)); + + OK(scad_stl_export(geom2, "/tmp/test", SCAD_FORCE_NORMALS_OUTWARD, 0)); + OK(scad_stl_export(geom2, "/tmp/test", SCAD_FORCE_NORMALS_OUTWARD, 1)); + OK(scad_stl_export(geom1, NULL, SCAD_FORCE_NORMALS_OUTWARD, 0)); + OK(scad_stl_export(geom1, NULL, SCAD_FORCE_NORMALS_OUTWARD, 1)); + + OK(scad_finalize()); + darray_double_release(&trg); + + 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 @@ -46,18 +46,21 @@ two_geoms double d1[3] = {1, 1, 1}; double p2[3]; double d2[3] = {0.5, 0.5, 0.5}; - struct scad_geometry* geoms[2]; - struct scad_geometry* out_geoms[2]; + struct scad_geometry* geoms[2] = {NULL, NULL}; + struct scad_geometry* out_geoms[2] = {NULL, NULL}; char name[64]; + struct scad_options options = SCAD_DEFAULT_OPTIONS; res_T res; OK(scad_initialize(NULL, allocator, 3)); + options.Misc.LogRefCounting = SCAD_LOG_DIMTAGS_ALL | SCAD_LOG_GEOMETRY; + OK(scad_set_options(&options)); /* set position for cube #2 */ d3(p2, x, 0.25, 0.25); - OK(scad_add_box("cube1", p1, d1, geoms+0)); - OK(scad_add_box("cube2", p2, d2, geoms+1)); + OK(scad_add_box(p1, d1, geoms+0)); + OK(scad_add_box(p2, d2, geoms+1)); /* Try to partition. * Fails if the 2 cubes overlap and overlapping is not allowed. */ @@ -75,26 +78,30 @@ two_geoms * geometry of both cubes does not define and inside/outside and the * partitioned geometry cannot have its normals forced */ if(x == 0.9) { - BAD(scad_stl_export(out_geoms[0], name, Scad_force_normals_outward, 0)); - OK(scad_stl_export(out_geoms[0], name, Scad_keep_normals_unchanged, 0)); + BAD(scad_stl_export(out_geoms[0], name, SCAD_FORCE_NORMALS_OUTWARD, 0)); + OK(scad_stl_export(out_geoms[0], name, SCAD_KEEP_NORMALS_UNCHANGED, 0)); } else { - OK(scad_stl_export(out_geoms[0], name, Scad_force_normals_outward, 1)); + OK(scad_stl_export(out_geoms[0], name, SCAD_FORCE_NORMALS_OUTWARD, 1)); } snprintf(name, sizeof(name), "part_%g_2o", x); if(x == 0.9) { - BAD(scad_stl_export(out_geoms[1], name, Scad_force_normals_outward, 1)); - OK(scad_stl_export(out_geoms[1], name, Scad_keep_normals_unchanged, 1)); + BAD(scad_stl_export(out_geoms[1], name, SCAD_FORCE_NORMALS_OUTWARD, 1)); + OK(scad_stl_export(out_geoms[1], name, SCAD_KEEP_NORMALS_UNCHANGED, 1)); } else { - OK(scad_stl_export(out_geoms[1], name, Scad_force_normals_outward, 1)); + OK(scad_stl_export(out_geoms[1], name, SCAD_FORCE_NORMALS_OUTWARD, 1)); } } else { snprintf(name, sizeof(name), "part_%g_1", x); - OK(scad_stl_export(geoms[0], name, Scad_force_normals_outward, 0)); + 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, Scad_force_normals_outward, 1)); + OK(scad_stl_export(geoms[1], name, SCAD_FORCE_NORMALS_OUTWARD, 1)); } end: + if(geoms[0]) OK(scad_geometry_ref_put(geoms[0])); + if(geoms[1]) OK(scad_geometry_ref_put(geoms[1])); + if(out_geoms[0]) OK(scad_geometry_ref_put(out_geoms[0])); + if(out_geoms[1]) OK(scad_geometry_ref_put(out_geoms[1])); OK(scad_finalize()); return res; } diff --git a/src/test_periodic.c b/src/test_periodic.c @@ -29,72 +29,74 @@ int main(int argc, char* argv[]) { struct mem_allocator allocator; - struct scad_geometry *cyl1 = NULL, *cyl2 = NULL, *cyl = NULL; - struct scad_geometry *bound = NULL, **faces = NULL; - struct scad_geometry * internal = NULL, *external = NULL, *lat[2] = { NULL, NULL}; + struct scad_geometry *cyl1 = NULL, *cyl2 = NULL, *cyl = NULL; + struct scad_geometry** faces = NULL; + struct scad_geometry *internal = NULL, *external = NULL, *lat[2] = { NULL, NULL}; double p1[3] = { 0,0,0 }, p2[3], d2[3] = { L,0,0}; double r1 = 1, r2 = r1 * XR, len; double cyl_affine[16] = { 1, 0, 0, 0, 0, XR, 0, 0, 0, 0, XR, 0, 0, 0, 0, 1 }; size_t i, facesn; + struct scad_options options = SCAD_DEFAULT_OPTIONS; (void)argc; (void)argv; OK(mem_init_proxy_allocator(&allocator, &mem_default_allocator)); - scad_initialize(NULL, &allocator, 2); + OK(scad_initialize(NULL, &allocator, 2)); + options.Misc.LogRefCounting = SCAD_LOG_DIMTAGS_ALL | SCAD_LOG_GEOMETRY; + OK(scad_set_options(&options)); + r2 = r1 * XR; - scad_add_cylinder(NULL, p1, d2, r1, 2*PI, &cyl1); - scad_add_cylinder(NULL, p1, d2, r2, 2*PI, &cyl2); - scad_cut_geometries("cylinder", &cyl2, 1, &cyl1, 1, &cyl); - scad_geometry_ref_put(cyl1); - scad_geometry_ref_put(cyl2); - scad_geometry_boundary(NULL, &cyl, 1, &bound); - scad_geometry_explode(bound, NULL, &faces, &facesn); - scad_geometry_ref_put(bound); + OK(scad_add_cylinder(p1, d2, r1, 2*PI, &cyl1)); + OK(scad_add_cylinder(p1, d2, r2, 2*PI, &cyl2)); + OK(scad_geometries_cut(&cyl2, 1, &cyl1, 1, &cyl)); + OK(scad_geometry_ref_put(cyl1)); + OK(scad_geometry_ref_put(cyl2)); + OK(scad_geometries_boundaries(&cyl, 1, &faces, &facesn)); ASSERT(facesn == 4); d3_add(p2, p1, d2); len = d3_len(d2); for(i = 0; i < facesn; i++) { struct scad_geometry* f = faces[i]; double center[3], m; - scad_geometry_get_centerofmass(f, center); + OK(scad_geometry_get_centerofmass(f, center)); if(fabs(center[0] - p1[0]) < FLT_EPSILON) { ASSERT(lat[0] == NULL); lat[0] = f; - scad_geometry_rename(f, "left_side"); + OK(scad_geometry_set_name(f, "left_side")); continue; } if(fabs(center[0] - p2[0]) < FLT_EPSILON) { ASSERT(lat[1] == NULL); lat[1] = f; - scad_geometry_rename(f, "right_side"); + OK(scad_geometry_set_name(f, "right_side")); continue; } scad_geometry_get_mass(f, &m); if(fabs(m - len*2*PI*r1) < FLT_EPSILON) { ASSERT(internal == NULL); internal = f; - scad_geometry_rename(f, "internal"); + OK(scad_geometry_set_name(f, "internal")); continue; } if(fabs(m - len*2*PI*r2) < FLT_EPSILON) { ASSERT(external == NULL); external = f; - scad_geometry_rename(f, "external"); + OK(scad_geometry_set_name(f, "external")); continue; } } ASSERT(lat[0] && lat[1] && internal && external); - scad_geometries_set_periodic(&internal, 1, &external, 1, cyl_affine); - scad_geometries_set_mesh_algorithm(lat, 1, Scad_Initial_Mesh_Only); + OK(scad_geometries_set_periodic(&internal, 1, &external, 1, cyl_affine)); + OK(scad_geometries_set_mesh_algorithm(lat, 1, SCAD_INITIAL_MESH_ONLY)); for(i = 0; i < facesn; i++) { - scad_geometry_ref_put(faces[i]); + OK(scad_geometry_ref_put(faces[i])); } MEM_RM(&allocator, faces); - scad_scene_mesh(); - scad_stl_export(cyl, "periodic", Scad_force_normals_outward, 1); - scad_geometry_ref_put(cyl); - scad_finalize(); + OK(scad_scene_mesh()); + OK(scad_stl_export(cyl, "periodic", SCAD_FORCE_NORMALS_OUTWARD, 1)); + OK(scad_geometry_ref_put(cyl)); + OK(scad_finalize()); #undef XR check_memory_allocator(&allocator); diff --git a/src/test_sync.c b/src/test_sync.c @@ -0,0 +1,169 @@ +/* Copyright (C) 2022-2025 |Méso|Star> (contact@meso-star.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#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/double3.h> +#include <rsys/mem_allocator.h> +#include <rsys/dynamic_array_double.h> + +#include <stdlib.h> +#include <stdio.h> + +static void +get_position + (const size_t ivert, double pos[2], void* data) +{ + double* coord = data; + ASSERT(pos && coord); + pos[0] = coord[2*ivert]; + pos[1] = coord[1+2*ivert]; +} + +int +main(int argc, char* argv[]) +{ + res_T res = RES_OK; + const double p1[3] = {0, 0, 0}; + const double p2[3] = {0.25, 0.25, 0.8}; + const double d1[3] = {1, 1, 1}; + double coord[] = {0, 1.6, 0.5, 0.9, 0.8, 0.6}; + struct scad_geometry* geom1 = NULL; + struct scad_geometry* geom2 = NULL; + struct scad_geometry* geom = NULL; + struct scad_geometry* poly = NULL; + struct mem_allocator allocator; + struct darray_double trg; + struct scad_options options = SCAD_DEFAULT_OPTIONS; + size_t c; + + (void)argc; (void)argv; + + OK(mem_init_proxy_allocator(&allocator, &mem_default_allocator)); + + OK(scad_initialize(NULL, &allocator, 3)); + options.Mesh.MeshSizeExtendFromBoundary = 0; + options.Mesh.MeshSizeFromPoints = 0; + options.Mesh.Algorithm = SCAD_INITIAL_MESH_ONLY; + OK(scad_set_options(&options)); + + OK(scad_add_box(p1, d1, &geom)); + OK(scad_scene_mesh()); + darray_double_init(&allocator, &trg); + OK(scad_stl_get_data(geom, &trg)); + CHK(12 * 9 == darray_double_size_get(&trg)); + darray_double_release(&trg); + OK(scad_geometry_ref_put(geom)); + + OK(scad_add_box(p1, d1, &geom1)); + OK(scad_add_box(p2, d1, &geom2)); + OK(scad_geometries_cut(&geom1, 1, &geom2, 1, &geom)); + OK(scad_geometry_ref_put(geom1)); + OK(scad_geometry_ref_put(geom2)); + OK(scad_scene_mesh()); + darray_double_init(&allocator, &trg); + OK(scad_stl_get_data(geom, &trg)); + CHK(24 * 9 == darray_double_size_get(&trg)); + darray_double_release(&trg); + OK(scad_geometry_ref_put(geom)); + + OK(scad_add_polygon(get_position, coord, 0, 3, &poly)); + OK(scad_geometry_extrude(poly, d1, &geom)); + OK(scad_geometry_ref_put(poly)); + OK(scad_scene_mesh()); + darray_double_init(&allocator, &trg); + OK(scad_stl_get_data(geom, &trg)); + CHK(8 * 9 == darray_double_size_get(&trg)); + darray_double_release(&trg); + OK(scad_geometry_ref_put(geom)); + + OK(scad_add_box(p1, d1, &geom1)); + OK(scad_geometry_translate(geom1, d1, &geom)); + OK(scad_geometry_ref_put(geom1)); + OK(scad_scene_mesh()); + darray_double_init(&allocator, &trg); + OK(scad_stl_get_data(geom, &trg)); + CHK(12 * 9 == darray_double_size_get(&trg)); + darray_double_release(&trg); + OK(scad_geometry_ref_put(geom)); + + OK(scad_add_box(p1, d1, &geom)); + OK(scad_geometry_set_visibility(geom, 0, 1)); + OK(scad_scene_mesh()); + darray_double_init(&allocator, &trg); + OK(scad_stl_get_data(geom, &trg)); + CHK(0 == darray_double_size_get(&trg)); + darray_double_release(&trg); + OK(scad_geometry_ref_put(geom)); + + OK(scad_add_box(p1, d1, &geom)); + OK(scad_scene_mesh()); + OK(scad_geometries_clear_mesh(&geom, 1)); + darray_double_init(&allocator, &trg); + OK(scad_stl_get_data(geom, &trg)); + CHK(0 == darray_double_size_get(&trg)); + darray_double_release(&trg); + OK(scad_geometry_ref_put(geom)); + + options.Mesh.Algorithm = SCAD_MESHADAPT; + options.Mesh.MeshSizeFromPoints = 1; + options.Mesh.MeshSizeExtendFromBoundary = 1; + OK(scad_set_options(&options)); + + OK(scad_add_box(p1, d1, &geom)); + OK(scad_scene_mesh()); + darray_double_init(&allocator, &trg); + OK(scad_stl_get_data(geom, &trg)); + c = darray_double_size_get(&trg); + darray_double_clear(&trg); + printf("%ld trg\n", c/9); + + OK(scad_geometries_clear_mesh(&geom, 1)); + OK(scad_stl_get_data(geom, &trg)); + CHK(0 == darray_double_size_get(&trg)); + + OK(scad_geometries_set_mesh_size_modifier(&geom, 1, SCAD_SIZE_FACTOR, 0.25)); + OK(scad_scene_mesh()); + OK(scad_stl_get_data(geom, &trg)); + printf("%ld trg\n", darray_double_size_get(&trg)/9); + CHK(c < darray_double_size_get(&trg)); + darray_double_release(&trg); + OK(scad_geometry_ref_put(geom)); + + options.Mesh.MeshSizeExtendFromBoundary = 0; + options.Mesh.MeshSizeFromPoints = 0; + options.Mesh.Algorithm = SCAD_INITIAL_MESH_ONLY; + OK(scad_set_options(&options)); + +#if 0 +set mesh size cb : besoin de sync ? +#endif + + options.Misc.LogRefCounting = SCAD_LOG_DIMTAGS_ONLY_UNDELETED; + OK(scad_set_options(&options)); + + 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_tolerance.c b/src/test_tolerance.c @@ -0,0 +1,99 @@ +/* Copyright (C) 2022-2024 |Méso|Star> (contact@meso-star.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#define _POSIX_C_SOURCE 200112L + +#include "scad.h" +#include "scad_geometry.h" +#include "test_common.h" + +#include <rsys/rsys.h> +#include <rsys/math.h> +#include <rsys/mem_allocator.h> +#include <rsys/double3.h> + +#include <stdlib.h> +#include <stdio.h> + +/* + * +-------+ +-------+ +------+ + * | | +----+ | +----+ | +----+ + * | | | | | | | | || | + * | | +----+ | +----+ | +----+ + * +-------+ +-------+ +------+ + * x = 1.1 x = 1 x = 0.9 + */ + +static int +tolerance + (double x, + double tol, + size_t expected_count, + struct mem_allocator* allocator) +{ + double p1[3] = {0, 0, 0}; + double d[3] = {1, 1, 1}; + double p2[3]; + struct scad_geometry* geoms[2]; + struct scad_geometry* out_geom; + size_t c; + + ASSERT(tol > 0 && (expected_count == 1 || expected_count == 2)); + + OK(scad_initialize(NULL, allocator, 3)); + + /* set position for cube #2 */ + d3(p2, x, 0, 0); + + OK(scad_add_box("cube1", p1, d, geoms+0)); + OK(scad_add_box("cube2", p2, d, geoms+1)); + + /* Try to fuse the 2 cubes and count resulting connex components */ + OK(scad_fuse_geometries("fused", geoms, 1, geoms+1, 1, &out_geom)); + OK(scad_geometry_get_count(out_geom, &c)); + + OK(scad_finalize()); + + return (c == expected_count) ? RES_OK : RES_BAD_ARG; +} + +int +main(int argc, char* argv[]) +{ + struct mem_allocator allocator; + res_T res = RES_OK; + + (void)argc; (void)argv; + + OK(mem_init_proxy_allocator(&allocator, &mem_default_allocator)); + + /* With contacting geometries */ + OK(tolerance(1, 0.01, 1, &allocator)); + OK(tolerance(1, 0.1, 1, &allocator)); + + /* With overlapping geometries */ + OK(tolerance(0.9, 0.01, 1, &allocator)); + OK(tolerance(0.9, 0.1, 1, &allocator)); + + /* With distant geometries */ + OK(tolerance(1.1, 0.01, 2, &allocator)); + OK(tolerance(1.1, 0.1, 1, &allocator)); + + check_memory_allocator(&allocator); + mem_shutdown_proxy_allocator(&allocator); + CHK(mem_allocated_size() == 0); + + return (res == RES_OK) ? EXIT_SUCCESS : EXIT_FAILURE; +}